def testParseClientIdWithoutRequiredFields(self): """Tests ParseClientId() without required fields.""" client_id_dict = {} for key in common.CLIENT_ID_FIELDS.keys(): client_id_dict[key] = None client_id_dict['track'] = common.common.DEFAULT_TRACK # empty cid self.assertEqual(client_id_dict, common.ParseClientId('')) # empty cid with delimiters self.assertEqual(client_id_dict, common.ParseClientId('|||')) # cid with unknown key name client_id_dict['ASDFMOOCOW'] = '1' self.assertEqual(client_id_dict, common.ParseClientId('ASDFMOOCOW=1')) del client_id_dict['ASDFMOOCOW']
def testParseClientIdWithValidClientIdAllValidTracks(self): """Tests ParseClientId() with a valid client id; tests all valid tracks.""" client_id_str, client_id_dict = self._GetClientIdTestData() for track in common.common.TRACKS: cid = client_id_str % track client_id_dict['track'] = track self.assertEqual(client_id_dict, common.ParseClientId(cid))
def testParseClientIdOnCorp(self): """Tests ParseClientId with on_corp=1.""" client_id_str, client_id_dict = self._GetClientIdTestData() client_id_str = client_id_str.replace('on_corp=0', 'on_corp=1') client_id_dict['on_corp'] = True cid = client_id_str % 'stable' client_id_dict['track'] = 'stable' self.assertEqual(client_id_dict, common.ParseClientId(cid))
def testParseClientIdWithAppleSusTrue(self): """Tests ParseClientId with applesus=true.""" client_id_str, client_id_dict = self._GetClientIdTestData() client_id_str = client_id_str.replace('applesus=false', 'applesus=true') client_id_dict['applesus'] = True cid = client_id_str % 'stable' client_id_dict['track'] = 'stable' self.assertEqual(client_id_dict, common.ParseClientId(cid))
def testParseClientIdNoneBool(self): """Tests ParseClientId with on_corp=<missing>.""" client_id_str, client_id_dict = self._GetClientIdTestData() client_id_str = client_id_str.replace('on_corp=0', 'on_corp=') client_id_dict['on_corp'] = None cid = client_id_str % 'stable' client_id_dict['track'] = 'stable' self.assertEqual(client_id_dict, common.ParseClientId(cid))
def testParseClientIdWithInvalidType(self): """Tests ParseClientId() with an invalid type; checks for None.""" client_id_str, client_id_dict = self._GetClientIdTestData() client_id_str = client_id_str.replace('uptime=123.0', 'uptime=hello') client_id_dict['uptime'] = None for track in common.common.TRACKS: cid = client_id_str % track client_id_dict['track'] = track self.assertEqual(client_id_dict, common.ParseClientId(cid))
def testParseClientIdNewline(self): """Tests ParseClientId when newline in the string.""" client_id_str, client_id_dict = self._GetClientIdTestData() client_id_str = client_id_str.replace( 'hostname=foohost', 'hostname=foo\nhost') client_id_dict['hostname'] = 'foo_host' cid = client_id_str % 'stable' client_id_dict['track'] = 'stable' self.assertEqual(client_id_dict, common.ParseClientId(cid))
def testParseClientIdWithVeryLongStrValues(self): """Tests ParseClientId() with str values that are over 500 characters.""" long_owner = ''.join(str(i) for i in range(999)) client_id_str, client_id_dict = self._GetClientIdTestData() client_id_str = client_id_str.replace( 'owner=foouser', 'owner=%s' % long_owner) client_id_dict['owner'] = long_owner[:500] client_id_dict['track'] = common.common.DEFAULT_TRACK output = common.ParseClientId(client_id_str) for k in client_id_dict: self.assertEqual(client_id_dict[k], output.get(k))
def testParseClientIdWithUuidOverride(self): """Tests ParseClientId() with uuid override.""" uuid_override = 'foouuidbar' uuid_override_full = 'CN=%s' % uuid_override client_id_str, client_id_dict = self._GetClientIdTestData() client_id_dict['uuid'] = uuid_override for track in common.common.TRACKS: cid = client_id_str % track client_id_dict['track'] = track self.assertEqual( client_id_dict, common.ParseClientId(cid, uuid=uuid_override_full))
def testParseClientIdWithUnicode(self): """Tests ParseClientId with some unicode characters.""" client_id_str, client_id_dict = self._GetClientIdTestData() # Convert the client_id_str to unicode. client_id_unicode = client_id_str.decode('utf-8') # Replace foohost with a unicode O with umlaut, surrounded by zz. client_id_unicode = client_id_str.replace('foohost', u'zz\u00D6zz') cid = client_id_unicode % u'stable' client_id_dict[u'track'] = u'stable' client_id_dict[u'hostname'] = u'zz\xd6zz' self.assertEqual(client_id_dict, common.ParseClientId(cid))
def GetClientIdForRequest(request, session=None, client_id_str=None): """Returns a client_id dict for the given request. Args: request: webapp Request object. session: response from auth.DoAnyAuth(). client_id_str: str client id. Returns: a dict client_id. """ if hasattr(session, 'uuid'): # DoMunkiAuth returned session, override uuid # webapp's request.headers.get() returns None if the key doesn't exist # which breaks urllib.unquote(), so return empty string default instead. client_id = request.headers.get('X-munki-client-id', '') if not client_id: logging.warning('Client ID header missing: %s', session.uuid) client_id = urllib.unquote(client_id) client_id = common.ParseClientId(client_id, uuid=session.uuid) else: # DoUserAuth was called; setup client id client_id_str = urllib.unquote(client_id_str) client_id = common.ParseClientId(client_id_str) return client_id
def _SanitazeMunkiHeader(self, munki_header): """Leave required fields only.""" client_id = common.ParseClientId(munki_header) return 'os_version=%s|track=%s' % (client_id.get( 'os_version', ''), client_id.get('track', 'stable'))
def post(self): """Reports get handler. Returns: A webapp.Response() response. """ session = gaeserver.DoMunkiAuth() uuid = main_common.SanitizeUUID(session.uuid) report_type = self.request.get('_report_type') feedback_requested = self.request.get('_feedback') message = None details = None client_id = None computer = None if report_type == 'preflight' or report_type == 'postflight': client_id_str = urllib.unquote(self.request.get('client_id')) client_id = common.ParseClientId(client_id_str, uuid=uuid) user_settings_str = self.request.get('user_settings') user_settings = None try: if user_settings_str: user_settings = util.Deserialize( urllib.unquote(str(user_settings_str))) except util.DeserializeError: logging.warning('Client %s sent broken user_settings: %s', client_id_str, user_settings_str) pkgs_to_install = self.request.get_all('pkgs_to_install') apple_updates_to_install = self.request.get_all( 'apple_updates_to_install') computer = models.Computer.get_by_key_name(uuid) ip_address = os.environ.get('REMOTE_ADDR', '') report_feedback = None if report_type == 'preflight': # if the UUID is known to be lost/stolen, log this connection. if models.ComputerLostStolen.IsLostStolen(uuid): logging.warning('Connection from lost/stolen machine: %s', uuid) models.ComputerLostStolen.LogLostStolenConnection( computer=computer, ip_address=ip_address) # we want to get feedback now, before preflight_datetime changes. if feedback_requested: client_exit = self.request.get('client_exit', None) report_feedback = self.GetReportFeedback( uuid, report_type, computer=computer, ip_address=ip_address, client_exit=client_exit) self.response.out.write(report_feedback) # if report feedback calls for a client exit, log it. if report_feedback == common.ReportFeedback.EXIT: if not client_exit: # client didn't ask for an exit, which means server decided. client_exit = 'Connection from defined exit IP address' common.WriteClientLog(models.PreflightExitLog, uuid, computer=computer, exit_reason=client_exit) common.LogClientConnection(report_type, client_id, user_settings, pkgs_to_install, apple_updates_to_install, computer=computer, ip_address=ip_address, report_feedback=report_feedback) elif report_type == 'install_report': computer = models.Computer.get_by_key_name(uuid) self._LogInstalls(self.request.get_all('installs'), computer) for removal in self.request.get_all('removals'): common.WriteClientLog(models.ClientLog, uuid, computer=computer, action='removal', details=removal) for problem in self.request.get_all('problem_installs'): common.WriteClientLog(models.ClientLog, uuid, computer=computer, action='install_problem', details=problem) elif report_type == 'preflight_exit': # NOTE(user): only remains for older clients. message = self.request.get('message') computer = common.WriteClientLog(models.PreflightExitLog, uuid, exit_reason=message) elif report_type == 'broken_client': # Default reason of "objc" to support legacy clients, existing when objc # was the only broken state ever reported. reason = self.request.get('reason', 'objc') details = self.request.get('details') logging.warning('Broken Munki client (%s): %s', reason, details) common.WriteBrokenClient(uuid, reason, details) elif report_type == 'msu_log': details = {} for k in ['time', 'user', 'source', 'event', 'desc']: details[k] = self.request.get(k, None) common.WriteComputerMSULog(uuid, details) else: # unknown report type; log all post params. params = [] for param in self.request.arguments(): params.append('%s=%s' % (param, self.request.get_all(param))) common.WriteClientLog(models.ClientLog, uuid, action='unknown', details=str(params)) # If the client asked for feedback, get feedback and respond. # Skip this if the report_type is preflight, as report feedback was # retrieved before LogComputerConnection changed preflight_datetime. if feedback_requested and report_type != 'preflight': self.response.out.write( self.GetReportFeedback( uuid, report_type, message=message, details=details, computer=computer, ))
def post(self): """Reports get handler. Returns: A webapp.Response() response. """ session = gaeserver.DoMunkiAuth() uuid = main_common.SanitizeUUID(session.uuid) report_type = self.request.get('_report_type') report_feedback = {} message = None details = None client_id = None computer = None if report_type == 'preflight' or report_type == 'postflight': client_id_str = urllib.unquote(self.request.get('client_id')) client_id = common.ParseClientId(client_id_str, uuid=uuid) user_settings_str = self.request.get('user_settings') user_settings = None try: if user_settings_str: user_settings = util.Deserialize( urllib.unquote(str(user_settings_str))) except util.DeserializeError: logging.warning('Client %s sent broken user_settings: %s', client_id_str, user_settings_str) pkgs_to_install = self.request.get_all('pkgs_to_install') apple_updates_to_install = self.request.get_all( 'apple_updates_to_install') computer = models.Computer.get_by_key_name(uuid) ip_address = os.environ.get('REMOTE_ADDR', '') if report_type == 'preflight': # we want to get feedback now, before preflight_datetime changes. client_exit = self.request.get('client_exit', None) report_feedback = self.GetReportFeedback( uuid, report_type, computer=computer, ip_address=ip_address, client_exit=client_exit) if self.request.get('json') == '1': self.response.out.write(JSON_PREFIX + json.dumps(report_feedback)) else: # For legacy clients that accept a single string, not JSON. feedback_to_send = 'OK' for feedback in LEGACY_FEEDBACK_LIST: if report_feedback.get(feedback.lower()): feedback_to_send = feedback self.response.out.write(feedback_to_send) # if report feedback calls for a client exit, log it. if report_feedback.get('exit'): if not client_exit: # client didn't ask for an exit, which means server decided. client_exit = 'Connection from defined exit IP address' common.WriteClientLog(models.PreflightExitLog, uuid, computer=computer, exit_reason=client_exit) common.LogClientConnection(report_type, client_id, user_settings, pkgs_to_install, apple_updates_to_install, computer=computer, ip_address=ip_address, report_feedback=report_feedback) elif report_type == 'install_report': computer = models.Computer.get_by_key_name(uuid) self._LogInstalls(self.request.get_all('installs'), computer) for removal in self.request.get_all('removals'): common.WriteClientLog(models.ClientLog, uuid, computer=computer, action='removal', details=removal) for problem in self.request.get_all('problem_installs'): common.WriteClientLog(models.ClientLog, uuid, computer=computer, action='install_problem', details=problem) elif report_type == 'broken_client': # Default reason of "objc" to support legacy clients, existing when objc # was the only broken state ever reported. reason = self.request.get('reason', 'objc') details = self.request.get('details') logging.warning('Broken Munki client (%s): %s', reason, details) common.WriteBrokenClient(uuid, reason, details) elif report_type == 'msu_log': details = {} for k in ['time', 'user', 'source', 'event', 'desc']: details[k] = self.request.get(k, None) common.WriteComputerMSULog(uuid, details) else: # unknown report type; log all post params. params = [] for param in self.request.arguments(): params.append('%s=%s' % (param, self.request.get_all(param))) common.WriteClientLog(models.ClientLog, uuid, action='unknown', details=str(params))