def ProcessResponse(self, response): """Sends an email for each response.""" emails_left = self.IncrementCounter() if emails_left < 0: return client_id = response.source client = aff4.FACTORY.Open(client_id, token=self.token) hostname = client.Get(client.Schema.HOSTNAME) or "unknown hostname" client_fragment_id = "/clients/%s" % client_id.Basename() if emails_left == 0: additional_message = (self.too_many_mails_msg % self.state.args.emails_limit) else: additional_message = "" subject = self.__class__.subject_template.render( source_urn=utils.SmartUnicode(self.state.source_urn)) body = self.__class__.template.render( client_id=client_id, client_fragment_id=client_fragment_id, admin_ui_url=config.CONFIG["AdminUI.url"], source_urn=self.state.source_urn, additional_message=additional_message, signature=config.CONFIG["Email.signature"], hostname=utils.SmartUnicode(hostname), creator=utils.SmartUnicode(self.token.username)) email_alerts.EMAIL_ALERTER.SendEmail(self.state.args.email_address, "grr-noreply", utils.SmartStr(subject), utils.SmartStr(body), is_html=True)
def GetValues(self, subject, attribute, start, end, limit=None): """Returns the values of the attribute between 'start' and 'end'. Args: subject: The subject. attribute: The attribute. start: The start timestamp. end: The end timestamp. limit: The maximum number of values to return. Returns: A list of the form (value, timestamp). """ subject = utils.SmartStr(subject) attribute = utils.SmartStr(attribute) query = """SELECT value, timestamp FROM tbl WHERE subject = ? AND predicate = ? AND timestamp >= ? AND timestamp <= ? ORDER BY timestamp""" if limit: query += " LIMIT ?" args = (subject, attribute, start, end, limit) else: args = (subject, attribute, start, end) data = self.Execute(query, args).fetchall() return data
def _CheckAccess(self, username, subject_id, approval_type): """Checks access to a given subject by a given user.""" cache_key = (username, subject_id, approval_type) try: self.acl_cache.Get(cache_key) stats.STATS.IncrementCounter("approval_searches", fields=["-", "cache"]) return True except KeyError: stats.STATS.IncrementCounter("approval_searches", fields=["-", "reldb"]) approvals = data_store.REL_DB.ReadApprovalRequests( utils.SmartStr(username), approval_type, subject_id=subject_id, include_expired=False) errors = [] for approval in approvals: try: approval_checks.CheckApprovalRequest(approval) self.acl_cache.Put(cache_key, True) return except access_control.UnauthorizedAccess as e: errors.append(e) subject = approval_checks.BuildLegacySubject(subject_id, approval_type) if not errors: raise access_control.UnauthorizedAccess("No approval found.", subject=subject) else: raise access_control.UnauthorizedAccess(" ".join( utils.SmartStr(e) for e in errors), subject=subject)
def Get(self, subject): """This will create the connection if needed so should not fail.""" filename, directory = common.ResolveSubjectDestination( subject, self.path_regexes) key = common.MakeDestinationKey(directory, filename) try: return super(SqliteConnectionCache, self).Get(key) except KeyError: dirname = utils.JoinPath(self.root_path, directory) path = utils.JoinPath(dirname, filename) + SQLITE_EXTENSION dirname = utils.SmartStr(dirname) path = utils.SmartStr(path) # Make sure directory exists. if not os.path.isdir(dirname): try: os.makedirs(dirname) except OSError: pass self._EnsureDatabaseExists(path) connection = SqliteConnection(path) super(SqliteConnectionCache, self).Put(key, connection) return connection
def testCreatesTarContainingFilesAndClientInfosAndManifest(self): self._InitializeFiles(hashing=True) fd_path = self._GenerateArchive(self.stat_entries, archive_format=api_call_handler_utils. CollectionArchiveGenerator.TAR_GZ) with tarfile.open(fd_path) as tar_fd: self.assertEqual( tar_fd.extractfile(utils.SmartStr( self.archive_paths[0])).read(), "hello1") self.assertEqual( tar_fd.extractfile(utils.SmartStr( self.archive_paths[1])).read(), "hello2") manifest_fd = tar_fd.extractfile("test_prefix/MANIFEST") self.assertEqual( yaml.safe_load(manifest_fd.read()), { "description": "Test description", "processed_files": 2, "archived_files": 2, "ignored_files": 0, "failed_files": 0 }) client_info_name = ("test_prefix/%s/client_info.yaml" % self.client_id.Basename()) client_info = yaml.safe_load( tar_fd.extractfile(client_info_name).read()) self.assertEqual(client_info["system_info"]["fqdn"], "Host-0.example.com")
def Handle(self, args, token=None): if not args.client_id: raise ValueError("client_id must be provided") flow_name = args.flow.name if not flow_name: flow_name = args.flow.runner_args.flow_name if not flow_name: raise RuntimeError("Flow name is not specified.") # Clear all fields marked with HIDDEN, except for output_plugins - they are # marked HIDDEN, because we have a separate UI for them, not because they # shouldn't be shown to the user at all. # # TODO(user): Refactor the code to remove the HIDDEN label from # FlowRunnerArgs.output_plugins. args.flow.runner_args.ClearFieldsWithLabel( rdf_structs.SemanticDescriptor.Labels.HIDDEN, exceptions="output_plugins") if args.original_flow: args.flow.runner_args.original_flow = rdf_objects.FlowReference( flow_id=utils.SmartStr(args.original_flow.flow_id), client_id=utils.SmartStr(args.original_flow.client_id)) flow_id = flow.StartFlow( client_id=args.client_id.ToClientURN(), flow_name=flow_name, token=token, args=args.flow.args, runner_args=args.flow.runner_args) fd = aff4.FACTORY.Open(flow_id, aff4_type=flow.GRRFlow, token=token) return ApiFlow().InitFromAff4Object(fd, flow_id=flow_id.Basename())
def BuildVarzJsonString(): """Builds Varz JSON string from all stats metrics.""" results = {} for name, metric_info in stats.STATS.GetAllMetricsMetadata().iteritems(): info_dict = dict(metric_type=metric_info.metric_type.name) if metric_info.value_type: info_dict["value_type"] = metric_info.value_type.name if metric_info.docstring: info_dict["docstring"] = metric_info.docstring if metric_info.units: info_dict["units"] = metric_info.units.name if metric_info.fields_defs: info_dict["fields_defs"] = [] for field_def in metric_info.fields_defs: info_dict["fields_defs"].append((field_def.field_name, utils.SmartStr(field_def.field_type))) value = {} all_fields = stats.STATS.GetMetricFields(name) for f in all_fields: joined_fields = ":".join(utils.SmartStr(fname) for fname in f) value[joined_fields] = _JSONMetricValue(metric_info, stats.STATS.GetMetricValue( name, fields=f)) else: value = _JSONMetricValue(metric_info, stats.STATS.GetMetricValue(name)) results[name] = dict(info=info_dict, value=value) encoder = json.JSONEncoder() return encoder.encode(results)
def ConvertVFSGRRClient(client): """Converts from `VFSGRRClient` to `rdfvalues.objects.ClientSnapshot`.""" snapshot = rdf_objects.ClientSnapshot(client_id=client.urn.Basename()) snapshot.filesystems = client.Get(client.Schema.FILESYSTEM) snapshot.hostname = client.Get(client.Schema.HOSTNAME) snapshot.fqdn = client.Get(client.Schema.FQDN) snapshot.system = client.Get(client.Schema.SYSTEM) snapshot.os_release = client.Get(client.Schema.OS_RELEASE) snapshot.os_version = utils.SmartStr(client.Get(client.Schema.OS_VERSION)) snapshot.arch = client.Get(client.Schema.ARCH) snapshot.install_time = client.Get(client.Schema.INSTALL_DATE) snapshot.knowledge_base = client.Get(client.Schema.KNOWLEDGE_BASE) snapshot.startup_info.boot_time = client.Get(client.Schema.LAST_BOOT_TIME) snapshot.startup_info.client_info = client.Get(client.Schema.CLIENT_INFO) conf = client.Get(client.Schema.GRR_CONFIGURATION) or [] for key in conf or []: snapshot.grr_configuration.Append(key=key, value=utils.SmartStr(conf[key])) lib = client.Get(client.Schema.LIBRARY_VERSIONS) or [] for key in lib or []: snapshot.library_versions.Append(key=key, value=utils.SmartStr(lib[key])) snapshot.kernel = client.Get(client.Schema.KERNEL) snapshot.volumes = client.Get(client.Schema.VOLUMES) snapshot.interfaces = client.Get(client.Schema.INTERFACES) snapshot.hardware_info = client.Get(client.Schema.HARDWARE_INFO) snapshot.memory_size = client.Get(client.Schema.MEMORY_SIZE) snapshot.cloud_instance = client.Get(client.Schema.CLOUD_INSTANCE) return snapshot
def Handle(self, args, token=None): if not args.hunt_id: raise ValueError("hunt_id can't be None") if not args.client_id: raise ValueError("client_id can't be None") if not args.vfs_path: raise ValueError("vfs_path can't be None") if not args.timestamp: raise ValueError("timestamp can't be None") api_vfs.ValidateVfsPath(args.vfs_path) results = implementation.GRRHunt.ResultCollectionForHID( args.hunt_id.ToURN()) expected_aff4_path = args.client_id.ToClientURN().Add(args.vfs_path) # TODO(user): should after_timestamp be strictly less than the desired # timestamp. timestamp = rdfvalue.RDFDatetime(int(args.timestamp) - 1) # If the entry corresponding to a given path is not found within # MAX_RECORDS_TO_CHECK from a given timestamp, we report a 404. for _, item in results.Scan( after_timestamp=timestamp.AsMicrosecondsSinceEpoch(), max_records=self.MAX_RECORDS_TO_CHECK): try: # Do not pass the client id we got from the caller. This will # get filled automatically from the hunt results and we check # later that the aff4_path we get is the same as the one that # was requested. aff4_path = export.CollectionItemToAff4Path(item, client_id=None) except export.ItemNotExportableError: continue if aff4_path != expected_aff4_path: continue try: aff4_stream = aff4.FACTORY.Open( aff4_path, aff4_type=aff4.AFF4Stream, token=token) if not aff4_stream.GetContentAge(): break return api_call_handler_base.ApiBinaryStream( "%s_%s" % (args.client_id, utils.SmartStr(aff4_path.Basename())), content_generator=self._GenerateFile(aff4_stream), content_length=len(aff4_stream)) except aff4.InstantiationError: break raise HuntFileNotFoundError( "File %s with timestamp %s and client %s " "wasn't found among the results of hunt %s" % (utils.SmartStr(args.vfs_path), utils.SmartStr(args.timestamp), utils.SmartStr(args.client_id), utils.SmartStr(args.hunt_id)))
def DeleteAttribute(self, subject, attribute): """Deletes all values for the given subject/attribute.""" subject = utils.SmartStr(subject) attribute = utils.SmartStr(attribute) query = "DELETE FROM tbl WHERE subject = ? AND predicate = ?" args = (subject, attribute) self.Execute(query, args) self.dirty = True self.deleted += self.cursor.rowcount
def SetAttribute(self, subject, attribute, value, timestamp): """Sets subject's attribute value with the given timestamp.""" subject = utils.SmartStr(subject) attribute = utils.SmartStr(attribute) query = "INSERT INTO tbl VALUES (?, ?, ?, ?)" args = (subject, attribute, timestamp, value) self.Execute(query, args) self.dirty = True self.deleted = max(0, self.deleted - self.cursor.rowcount)
def DeleteAttributeRange(self, subject, attribute, start, end): """Deletes all values of a attribute within the range [start, end].""" subject = utils.SmartStr(subject) attribute = utils.SmartStr(attribute) query = """DELETE FROM tbl WHERE subject = ? AND predicate = ? AND timestamp >= ? AND timestamp <= ?""" args = (subject, attribute, int(start), int(end)) self.Execute(query, args) self.dirty = True self.deleted += self.cursor.rowcount
def testWinServicesParser(self): dword = rdf_client.StatEntry.RegistryType.REG_DWORD_LITTLE_ENDIAN reg_str = rdf_client.StatEntry.RegistryType.REG_SZ hklm = "HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services" hklm_set01 = "HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/services" service_keys = [ ("%s/ACPI/Type" % hklm, 1, dword), ("%s/ACPI/Start" % hklm, 0, dword), # This one is broken, the parser should just ignore it. ("%s/notarealservice" % hklm, 3, dword), ("%s/ACPI/ErrorControl" % hklm, 3, dword), ("%s/ACPI/ImagePath" % hklm, "system32\\drivers\\ACPI.sys", reg_str), ("%s/ACPI/DisplayName" % hklm, "Microsoft ACPI Driver", reg_str), ("%s/ACPI/Group" % hklm, "Boot Bus Extender", reg_str), ("%s/ACPI/DriverPackageId" % hklm, "acpi.inf_amd64_neutral_99aaaaabcccccccc", reg_str), ("%s/AcpiPmi/Start" % hklm_set01, 3, dword), ("%s/AcpiPmi/DisplayName" % hklm_set01, "AcpiPmi", rdf_client.StatEntry.RegistryType.REG_MULTI_SZ), (u"%s/中国日报/DisplayName" % hklm, u"中国日报", reg_str), (u"%s/中国日报/Parameters/ServiceDLL" % hklm, "blah.dll", reg_str) ] stats = [self._MakeRegStat(*x) for x in service_keys] parser = windows_registry_parser.WinServicesParser() results = parser.ParseMultiple(stats, None) names = [] for result in results: if result.display_name == u"中国日报": self.assertEqual(result.display_name, u"中国日报") self.assertEqual(result.service_dll, "blah.dll") names.append(result.display_name) elif utils.SmartStr(result.registry_key).endswith("AcpiPmi"): self.assertEqual(result.name, "AcpiPmi") self.assertEqual(result.startup_type, 3) self.assertEqual(result.display_name, "[u'AcpiPmi']") self.assertEqual(result.registry_key, "%s/AcpiPmi" % hklm_set01) names.append(result.display_name) elif utils.SmartStr(result.registry_key).endswith("ACPI"): self.assertEqual(result.name, "ACPI") self.assertEqual(result.service_type, 1) self.assertEqual(result.startup_type, 0) self.assertEqual(result.error_control, 3) self.assertEqual(result.image_path, "system32\\drivers\\ACPI.sys") self.assertEqual(result.display_name, "Microsoft ACPI Driver") self.assertEqual(result.group_name, "Boot Bus Extender") self.assertEqual(result.driver_package_id, "acpi.inf_amd64_neutral_99aaaaabcccccccc") names.append(result.display_name) self.assertItemsEqual(names, [u"中国日报", "[u'AcpiPmi']", "Microsoft ACPI Driver"])
def ScanAttributes(self, subject_prefix, attributes, after_urn=None, max_records=None): """Yields the values of attribute for a range of subjexts. Args: subject_prefix: Returns records for all subjects which begin with subject_prefix. attributes: A list of the attributes of interest. after_urn: If set, restrict to records which come after. max_records: The maximum number of values to return. Yields: Records of the form (subject, timestamp, value). """ # A generator cannot really be synchronized, and in any case, this might be # long running. So we just make our own cursor. cursor = self.conn.cursor() cursor.execute("PRAGMA synchronous = OFF") cursor.execute("PRAGMA cache_size = 10000") query = """SELECT t1.subject, t1.predicate, t1.timestamp, t1.value FROM tbl AS t1, (SELECT subject, predicate, MAX(timestamp) AS max_ts FROM tbl WHERE subject LIKE ? AND subject > ? AND predicate in (%s) GROUP BY subject, predicate) AS t2 WHERE t1.subject = t2.subject AND t1.timestamp = t2.max_ts AND t1.predicate = t2.predicate ORDER BY t1.subject """ % ",".join("?" * len(attributes)) subject_prefix = utils.SmartStr(subject_prefix) if after_urn: after_urn = utils.SmartStr(after_urn) else: after_urn = "" args = [subject_prefix + "%", after_urn] + attributes if max_records: query += " LIMIT ?" args.append(max_records * len(attributes)) cursor.execute(query, args) for r in cursor: yield r
def testEmailPluginSendsEmailPerEveyBatchOfResponses(self): self.ProcessResponses(plugin_args=email_plugin.EmailOutputPluginArgs( email_address=self.email_address), responses=[rdf_client.Process(pid=42)]) self.assertEqual(len(self.email_messages), 1) msg = self.email_messages[0] self.assertEqual(msg["address"], self.email_address) self.assertTrue("got a new result in %s" % self.results_urn in msg["title"]) self.assertTrue(utils.SmartStr(self.client_id) in msg["message"]) self.assertTrue(utils.SmartStr(self.hostname) in msg["message"])
def Handle(self, args, token=None): output_fname = re.sub( "[^0-9a-zA-Z]+", "_", "%s_%s" % (utils.SmartStr(args.client_id), utils.SmartStr(args.flow_id))) code_to_execute = ("""grrapi.Client("%s").Flow("%s").GetFilesArchive().""" """WriteToFile("./flow_results_%s.zip")""") % ( args.client_id, args.flow_id, output_fname) export_command_str = " ".join([ config.CONFIG["AdminUI.export_command"], "--exec_code", utils.ShellQuote(code_to_execute) ]) return ApiGetFlowResultsExportCommandResult(command=export_command_str)
def Done(self, responses): response = responses.First() if not responses.success: raise flow.FlowError("Execute Python hack failed: %s" % responses.status) if response: result = utils.SmartStr(response.return_val) # Send reply with full data, but only log the first 200 bytes. str_result = result[0:200] if len(result) >= 200: str_result += "...[truncated]" self.Log("Result: %s" % str_result) self.SendReply( rdfvalue.RDFBytes(utils.SmartStr(response.return_val)))
def ValidateVfsPath(path): """Validates a VFS path.""" components = (path or "").lstrip("/").split("/") if not components: raise ValueError("Empty path is not a valid path: %s." % utils.SmartStr(path)) if components[0] not in ROOT_FILES_WHITELIST: raise ValueError( "First path component was '%s', but has to be one of %s" % (utils.SmartStr(components[0]), ", ".join(ROOT_FILES_WHITELIST))) return True
def Grant(self): """Create the Approval object and notify the Approval Granter.""" approvals_root_urn = aff4.ROOT_URN.Add("ACL").Add( self.subject_urn.Path()).Add(self.delegate) children_urns = list(aff4.FACTORY.ListChildren(approvals_root_urn)) if not children_urns: raise access_control.UnauthorizedAccess( "No approval found for user %s" % utils.SmartStr(self.token.username), subject=self.subject_urn) approvals = aff4.FACTORY.MultiOpen(children_urns, mode="r", aff4_type=Approval, token=self.token) found_approval_urn = None for approval in approvals: approval_reason = approval.Get(approval.Schema.REASON) if (utils.SmartUnicode(approval_reason) == utils.SmartUnicode( self.reason) and (not found_approval_urn or approval_reason.age > found_approval_urn.age)): found_approval_urn = approval.urn found_approval_urn.age = approval_reason.age if not found_approval_urn: raise access_control.UnauthorizedAccess( "No approval with reason '%s' found for user %s" % (utils.SmartStr( self.reason), utils.SmartStr(self.token.username)), subject=self.subject_urn) # This object must already exist. try: approval_request = aff4.FACTORY.Open(found_approval_urn, mode="rw", aff4_type=self.approval_type, token=self.token) except IOError: raise access_control.UnauthorizedAccess( "Approval object does not exist.", requested_access="rw") with approval_request: # We are now an approver for this request. approval_request.AddAttribute( approval_request.Schema.APPROVER(self.token.username)) return found_approval_urn
def GetNewestValue(self, subject, attribute): """Returns the newest value for subject/attribute.""" subject = utils.SmartStr(subject) attribute = utils.SmartStr(attribute) query = """SELECT value, timestamp FROM tbl WHERE subject = ? AND predicate = ? ORDER BY timestamp DESC LIMIT 1""" args = (subject, attribute) data = self.Execute(query, args).fetchone() if data: return (data[0], data[1]) else: return None
def _VerifyHunts(self, hunts_plugins_by_verifier): results_by_hunt = {} errors = [] for verifier_cls, hunts_plugins in hunts_plugins_by_verifier.items(): if verifier_cls == self.NON_VERIFIABLE: for plugin_id, plugin_descriptor, plugin_obj, hunt in hunts_plugins: result = output_plugin.OutputPluginVerificationResult( status=output_plugin.OutputPluginVerificationResult.Status.N_A, status_message=("Plugin %s is not verifiable." % plugin_obj.__class__.__name__)) self._FillResult(result, plugin_id, plugin_descriptor) results_by_hunt.setdefault(hunt.urn, []).append(result) stats.STATS.IncrementCounter( "hunt_output_plugin_verifications", fields=[utils.SmartStr(result.status)]) continue verifier = verifier_cls() plugins_hunts_pairs = [] for plugin_id, plugin_descriptor, plugin_obj, hunt in hunts_plugins: plugins_hunts_pairs.append((plugin_obj, hunt)) try: for hunt_urn, result in verifier.MultiVerifyHuntOutput( plugins_hunts_pairs): self._FillResult(result, plugin_id, plugin_descriptor) results_by_hunt.setdefault(hunt.urn, []).append(result) stats.STATS.IncrementCounter( "hunt_output_plugin_verifications", fields=[utils.SmartStr(result.status)]) except output_plugin.MultiVerifyHuntOutputError as e: logging.exception(e) errors.extend(e.errors) stats.STATS.IncrementCounter( "hunt_output_plugin_verification_errors", delta=len(e.errors)) for hunt_urn, results in results_by_hunt.items(): yield hunt_urn, results if errors: raise MultiHuntVerificationSummaryError(errors)
def GenerateCSRFToken(user_id, time): """Generates a CSRF token based on a secret key, id and time.""" time = time or rdfvalue.RDFDatetime.Now().AsMicrosecondsSinceEpoch() secret = config.CONFIG.Get("AdminUI.csrf_secret_key", None) # TODO(amoser): Django is deprecated. Remove this at some point. if not secret: secret = config.CONFIG["AdminUI.django_secret_key"] digester = hmac.new(utils.SmartStr(secret), digestmod=hashlib.sha256) digester.update(utils.SmartStr(user_id)) digester.update(CSRF_DELIMITER) digester.update(str(time)) digest = digester.digest() token = base64.urlsafe_b64encode("%s%s%d" % (digest, CSRF_DELIMITER, time)) return token.rstrip("=")
def testFindDirectories(self): """Test that the Find flow works with directories.""" client_mock = action_mocks.ActionMock(searching.Find) # Prepare a findspec. findspec = rdf_client.FindSpec( path_regex="bin", pathspec=rdf_paths.PathSpec( path="/", pathtype=rdf_paths.PathSpec.PathType.OS)) session_id = flow_test_lib.TestFlowHelper(find.FindFiles.__name__, client_mock, client_id=self.client_id, token=self.token, findspec=findspec) # Check the results collection. fd = flow.GRRFlow.ResultCollectionForFID(session_id) # Make sure that bin is a directory self.assertEqual(len(fd), 2) for child in fd: path = utils.SmartStr(child.AFF4Path(self.client_id)) self.assertTrue("bin" in path) self.assertEqual(child.__class__.__name__, "StatEntry")
def _Encode(self, value): """Encode the value into a Binary BSON object. The data store only supports the following values: -Integer -Unicode -Bytes (python string) We preserve integers and unicode objects, but serialize anything else. Args: value: The value to be encoded. Returns: An encoded value. """ if isinstance(value, (basestring, int, float)): return value try: return value.SerializeToDataStore() except AttributeError: try: return value.SerializeToString() except AttributeError: return utils.SmartStr(value)
def testFindFilesWithGlob(self): """Test that the Find flow works with glob.""" client_mock = action_mocks.ActionMock(searching.Find) # Prepare a findspec. findspec = rdf_client.FindSpec( path_glob="bash*", pathspec=rdf_paths.PathSpec( path="/", pathtype=rdf_paths.PathSpec.PathType.OS)) session_id = flow_test_lib.TestFlowHelper(find.FindFiles.__name__, client_mock, client_id=self.client_id, token=self.token, findspec=findspec) # Check the results collection. fd = flow.GRRFlow.ResultCollectionForFID(session_id) # Make sure that bash is a file. matches = set([x.AFF4Path(self.client_id).Basename() for x in fd]) self.assertEqual(sorted(matches), ["bash"]) self.assertEqual(len(fd), 2) for child in fd: path = utils.SmartStr(child.AFF4Path(self.client_id)) self.assertTrue(path.endswith("bash")) self.assertEqual(child.__class__.__name__, "StatEntry")
def ScanAttributes(self, subject_prefix, attributes, after_urn="", max_records=None, relaxed_order=False): subject_prefix = utils.SmartStr(rdfvalue.RDFURN(subject_prefix)) if subject_prefix[-1] != "/": subject_prefix += "/" if after_urn: after_urn = utils.SmartUnicode(after_urn) subjects = [] for s in self.subjects.keys(): if s.startswith(subject_prefix) and s > after_urn: subjects.append(s) subjects.sort() return_count = 0 for s in subjects: if max_records and return_count >= max_records: break r = self.subjects[s] results = {} for attribute in attributes: attribute_list = r.get(attribute) if attribute_list: value, timestamp = attribute_list[-1] results[attribute] = (timestamp, value) if results: return_count += 1 yield (s, results)
def SetLock(self, subject, expires, token): """Locks a subject.""" subject = utils.SmartStr(subject) query = "INSERT OR REPLACE INTO lock VALUES(?, ?, ?)" args = (subject, expires, token) self.Execute(query, args) self.dirty = True
def RemoveLock(self, subject): """Removes the lock from a subject.""" subject = utils.SmartStr(subject) query = "DELETE FROM lock WHERE subject = ?" args = (subject,) self.Execute(query, args) self.dirty = True
def GetNewestFromPrefix(self, subject, prefix, limit=None): """Returns the newest values for attributes that match 'prefix'. Args: subject: The subject. prefix: The attribute prefix. limit: The maximum number of records to return. Returns: A list of the form (attribute, value, timestamp). """ pattern = prefix + "%" subject = utils.SmartStr(subject) query = """SELECT predicate, MAX(timestamp), value FROM tbl WHERE subject = ? AND predicate LIKE ? GROUP BY predicate""" if limit: query += " LIMIT ?" args = (subject, pattern, limit) else: args = (subject, pattern) # Reorder columns. data = self.Execute(query, args).fetchall() return [(pred, val, ts) for pred, ts, val in data]
def GetValuesFromPrefix(self, subject, prefix, start, end, limit=None): """Returns the values of the attributes that match 'prefix'. Args: subject: The subject. prefix: The attribute prefix. start: The start timestamp. end: The end timestamp. limit: The maximum number of values to return. Returns: A list of the form (attribute, value, timestamp). """ pattern = prefix + "%" subject = utils.SmartStr(subject) query = """SELECT predicate, value, timestamp FROM tbl WHERE subject = ? AND predicate LIKE ? AND timestamp >= ? AND timestamp <= ? ORDER BY timestamp DESC""" if limit: query += " LIMIT ?" args = (subject, pattern, start, end, limit) else: args = (subject, pattern, start, end) data = self.Execute(query, args).fetchall() return data