# Copyright (C) 2020, Carlos Garnacho # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # """ Test portal """ import gi gi.require_version("Tracker", "3.0") from gi.repository import GLib from gi.repository import Gio from gi.repository import Tracker import unittest import configuration import fixtures class TestPortal(fixtures.TrackerPortalTest): def test_01_forbidden(self): self.start_service("org.freedesktop.Inaccessible") self.assertRaises( GLib.Error, self.query, "org.freedesktop.Inaccessible", "select ?u { BIND (1 AS ?u) }", ) def test_02_allowed(self): self.start_service("org.freedesktop.PortalTest") res = self.query("org.freedesktop.PortalTest", "select ?u { BIND (1 AS ?u) }") self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "1") def test_03_graph_access(self): self.start_service("org.freedesktop.PortalTest") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Disallowed;" + "INSERT { GRAPH tracker:Disallowed { a nfo:FileDataObject } };" + "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { a nfo:FileDataObject } }", ) res = self.query( "org.freedesktop.PortalTest", "select ?u { ?u a rdfs:Resource }" ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "http://example/b") def test_04_rows_cols(self): self.start_service("org.freedesktop.PortalTest") res = self.query( "org.freedesktop.PortalTest", "select ?a ?b { VALUES (?a ?b) { (1 2) (3 4) (5 6) } }", ) self.assertEqual(len(res), 3) self.assertEqual(res[0][0], "1") self.assertEqual(res[0][1], "2") self.assertEqual(len(res[0]), 2) self.assertEqual(res[1][0], "3") self.assertEqual(res[1][1], "4") self.assertEqual(len(res[1]), 2) self.assertEqual(res[2][0], "5") self.assertEqual(res[2][1], "6") self.assertEqual(len(res[2]), 2) def __wait_for_notifier(self): """ In the callback of the signals, there should be a self.loop.quit () """ self.timeout_id = GLib.timeout_add_seconds( configuration.DEFAULT_TIMEOUT, self.__timeout_on_idle ) self.loop.run_checked() def __timeout_on_idle(self): self.loop.quit() self.fail( "Timeout, the signal never came after %i seconds!" % configuration.DEFAULT_TIMEOUT ) def __notifier_event_cb(self, notifier, service, graph, events): self.notifier_events += events if self.timeout_id != 0: GLib.source_remove(self.timeout_id) self.timeout_id = 0 self.loop.quit() def test_05_local_connection_notifier(self): self.start_service("org.freedesktop.PortalTest") self.notifier_events = [] conn = self.create_local_connection() notifier = conn.create_notifier() notifier.connect("events", self.__notifier_event_cb) signalId = notifier.signal_subscribe( self.bus, "org.freedesktop.PortalTest", None, "tracker:Allowed" ) signalId2 = notifier.signal_subscribe( self.bus, "org.freedesktop.PortalTest", None, "tracker:Disallowed" ) self.update( "org.freedesktop.PortalTest", "INSERT { GRAPH tracker:Disallowed { a nmm:MusicPiece } }", ) self.update( "org.freedesktop.PortalTest", "INSERT { GRAPH tracker:Allowed { a nmm:MusicPiece } }", ) self.__wait_for_notifier() notifier.signal_unsubscribe(signalId) notifier.signal_unsubscribe(signalId2) # Only one event is expected, from the allowed graph self.assertEqual(len(self.notifier_events), 1) self.assertEqual(self.notifier_events[0].get_urn(), "http://example/b") conn.close() def test_06_id_access(self): self.start_service("org.freedesktop.PortalTest") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { a nfo:FileDataObject } }", ) res = self.query( "org.freedesktop.PortalTest", "select tracker:id(xsd:string) tracker:uri(1) { }", ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "0") self.assertEqual(res[0][1], None) res = self.query( "org.freedesktop.PortalTest", "select tracker:id() tracker:uri(tracker:id()) { }", ) self.assertEqual(len(res), 1) self.assertNotEqual(res[0][0], "0") self.assertEqual(res[0][1], "http://example/b") def test_07_id_access_disallowed(self): self.start_service("org.freedesktop.PortalTest") # Insert resource into disallowed graph, ensure it is not visible self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Disallowed;" + "INSERT { GRAPH tracker:Disallowed { a nfo:FileDataObject } }", ) res = self.query( "org.freedesktop.PortalTest", "select tracker:id() tracker:uri(tracker:id()) { }", ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "0") self.assertIsNone(res[0][1]) # Insert same resource into allowed graph, ensure it is visible self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { a nfo:FileDataObject } }", ) res = self.query( "org.freedesktop.PortalTest", "select tracker:id() tracker:uri(tracker:id()) { }", ) self.assertEqual(len(res), 1) self.assertNotEqual(res[0][0], "0") self.assertEqual(res[0][1], "http://example/b") resourceId = res[0][0] # Delete resource from allowed graph, ensure it is not visible again self.update( "org.freedesktop.PortalTest", "DELETE { GRAPH tracker:Allowed { a rdfs:Resource } }", ) res = self.query( "org.freedesktop.PortalTest", "select tracker:id() tracker:uri(tracker:id()) tracker:uri(" + str(resourceId) + ") { }", ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "0") self.assertIsNone(res[0][1]) self.assertIsNone(res[0][2]) def test_08_local_connection_service(self): self.start_service("org.freedesktop.PortalTest") self.notifier_events = [] conn = self.create_local_connection() self.update( "org.freedesktop.PortalTest", "INSERT { GRAPH tracker:Disallowed { a nmm:MusicPiece } }", ) self.update( "org.freedesktop.PortalTest", "INSERT { GRAPH tracker:Allowed { a nmm:MusicPiece } }", ) # Only one resource is expected, from the allowed graph cursor = conn.query( "select ?u { SERVICE { ?u a nmm:MusicPiece } }" ) self.assertTrue(cursor.next()) self.assertEqual(cursor.get_string(0)[0], "http://example/b") self.assertFalse(cursor.next()) cursor.close() conn.close() # Test that all ways to specify a graph in the query hit a dead end def test_09_query_graphs(self): self.start_service("org.freedesktop.PortalTest") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Disallowed;" + "INSERT { GRAPH tracker:Disallowed { a nfo:FileDataObject } }", ) res = self.query( "org.freedesktop.PortalTest", "select ?u from tracker:Disallowed { ?u a rdfs:Resource }", ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", "select ?u { graph tracker:Disallowed { ?u a rdfs:Resource } }", ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", "constraint graph tracker:Disallowed select ?u { ?u a rdfs:Resource }", ) self.assertEqual(len(res), 0) # Test that it is not possible to break through into other services def test_10_query_services(self): self.start_service("org.freedesktop.PortalTest") self.start_service("org.freedesktop.InaccessibleService") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { a nfo:FileDataObject } }", ) self.update( "org.freedesktop.InaccessibleService", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { a nfo:FileDataObject } }", ) try: exception = None res = self.query( "org.freedesktop.PortalTest", "select ?u { service { ?u a rdfs:Resource } }", ) except Exception as e: exception = e finally: self.assertIsNotNone(exception) try: exception = None res = self.query( "org.freedesktop.InaccessibleService", "select ?u { ?u a rdfs:Resource }", ) except Exception as e: exception = e finally: self.assertIsNotNone(exception) # Test that property paths resolve correctly across allowed # and disallowed graphs def test_11_query_property_paths(self): self.start_service("org.freedesktop.PortalTest") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Disallowed;" + "INSERT { GRAPH tracker:Disallowed { " + ' a nfo:FileDataObject ; nfo:fileName "A" ; nie:interpretedAs .' + ' a nmm:MusicPiece ; nie:isStoredAs ; nie:title "title2" } }', ) # Test property paths with allowed/disallowed graphs in both ends res = self.query( "org.freedesktop.PortalTest", "select ?u ?t { ?u nie:interpretedAs/nie:title ?t }", ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", "select ?u ?fn { ?u nie:isStoredAs/nfo:fileName ?fn }", ) self.assertEqual(len(res), 0) # Insert a resource in the allowed graph self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { " + ' a nfo:FileDataObject ; nfo:fileName "A" ; nie:interpretedAs .' + ' a nmm:MusicPiece ; nie:isStoredAs ; nie:title "title1" } }', ) # Try the queries again res = self.query( "org.freedesktop.PortalTest", "select ?u ?t { ?u nie:interpretedAs/nie:title ?t }", ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "http://example/a") self.assertEqual(res[0][1], "title1") res = self.query( "org.freedesktop.PortalTest", "select ?u ?fn { ?u nie:isStoredAs/nfo:fileName ?fn }", ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "http://example/a1") self.assertEqual(res[0][1], "A") res = self.query( "org.freedesktop.PortalTest", "select ?u ?fn { GRAPH tracker:Disallowed { ?u nie:isStoredAs/nfo:fileName ?fn } }", ) self.assertEqual(len(res), 0) # Delete the allowed resource again self.update("org.freedesktop.PortalTest", "DROP GRAPH tracker:Allowed") # Query results should revert to the original values res = self.query( "org.freedesktop.PortalTest", "select ?u ?t { ?u nie:interpretedAs/nie:title ?t }", ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", "select ?u ?fn { ?u nie:isStoredAs/nfo:fileName ?fn }", ) self.assertEqual(len(res), 0) # Test that property paths resolve correctly across allowed # and disallowed graphs def test_12_query_fts(self): self.start_service("org.freedesktop.PortalTest") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Disallowed;" + "INSERT { GRAPH tracker:Disallowed { " + " a nfo:FileDataObject ; nie:interpretedAs ." + ' a nmm:MusicPiece ; nie:isStoredAs ; nie:title "apples and oranges" } }', ) # Query for both keywords, they are expected to be non-visible res = self.query( "org.freedesktop.PortalTest", 'select ?u { ?u fts:match "apples" }' ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", 'select ?u { ?u fts:match "oranges" }' ) self.assertEqual(len(res), 0) # Insert a resource in the allowed graph self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { " + ' a nfo:FileDataObject ; nfo:fileName "file name" ; nie:interpretedAs .' + ' a nmm:MusicPiece ; nie:isStoredAs ; nie:title "apples" } }', ) # Try the queries again, we should get a match from the allowed graph for 'apples' res = self.query( "org.freedesktop.PortalTest", 'select ?u { ?u fts:match "apples" }' ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "http://example/a1") res = self.query( "org.freedesktop.PortalTest", 'select ?u { ?u fts:match "oranges" }' ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", 'select ?u { GRAPH tracker:Disallowed { ?u fts:match "oranges" } }', ) self.assertEqual(len(res), 0) # Delete the allowed resource again self.update("org.freedesktop.PortalTest", "DROP GRAPH tracker:Allowed") # The query results should revert to the original values res = self.query( "org.freedesktop.PortalTest", 'select ?u { ?u fts:match "apples" }' ) self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", 'select ?u { ?u fts:match "oranges" }' ) self.assertEqual(len(res), 0) # Test that property paths resolve correctly across allowed # and disallowed graphs def test_13_query_unrestricted_triples(self): self.start_service("org.freedesktop.PortalTest") self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Disallowed;" + "INSERT { GRAPH tracker:Disallowed { " + ' a nfo:FileDataObject ; nfo:fileName "A" . } }', ) res = self.query("org.freedesktop.PortalTest", 'select ?s { ?s ?p "A" }') self.assertEqual(len(res), 0) res = self.query( "org.freedesktop.PortalTest", 'ASK { ?p "A" }' ) self.assertEqual(len(res), 1) self.assertNotEqual(res[0][0], "true") # Insert a resource in the allowed graph self.update( "org.freedesktop.PortalTest", "CREATE GRAPH tracker:Allowed;" + "INSERT { GRAPH tracker:Allowed { " + ' a nfo:FileDataObject ; nfo:fileName "A" . } }', ) # Try the queries again res = self.query("org.freedesktop.PortalTest", 'select ?s { ?s ?p "A" }') self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "http://example/a") res = self.query( "org.freedesktop.PortalTest", 'ASK { ?p "A" }' ) self.assertEqual(len(res), 1) self.assertEqual(res[0][0], "true") res = self.query( "org.freedesktop.PortalTest", 'select ?s { GRAPH tracker:Disallowed { ?s ?p "A" } }', ) self.assertEqual(len(res), 0) # Delete the allowed resource again self.update("org.freedesktop.PortalTest", "DROP GRAPH tracker:Allowed") # The query results should revert to the original values res = self.query("org.freedesktop.PortalTest", 'select ?s { ?s ?p "A" }') self.assertEqual(len(res), 0) if __name__ == "__main__": fixtures.tracker_test_main()