##// END OF EJS Templates
automatically cull oldest 25% of signatures...
Min RK -
Show More
@@ -102,14 +102,23 b' class NotebookNotary(LoggingConfigurable):'
102 app.initialize(argv=[])
102 app.initialize(argv=[])
103 return app.profile_dir
103 return app.profile_dir
104
104
105 db_file = Unicode(config=True)
105 db_file = Unicode(config=True,
106 help="""The sqlite file in which to store notebook signatures.
107 By default, this will be in your IPython profile.
108 You can set it to ':memory:' to disable sqlite writing to the filesystem.
109 """)
106 def _db_file_default(self):
110 def _db_file_default(self):
107 if self.profile_dir is None:
111 if self.profile_dir is None:
108 return ':memory:'
112 return ':memory:'
109 return os.path.join(self.profile_dir.security_dir, u'nbsignatures.db')
113 return os.path.join(self.profile_dir.security_dir, u'nbsignatures.db')
110
114
111 # 64k entries ~ 12MB
115 # 64k entries ~ 12MB
112 db_size_limit = Integer(65535, config=True)
116 cache_size = Integer(65535, config=True,
117 help="""The number of notebook signatures to cache.
118 When the number of signatures exceeds this value,
119 the oldest 25% of signatures will be culled.
120 """
121 )
113 db = Any()
122 db = Any()
114 def _db_default(self):
123 def _db_default(self):
115 if sqlite3 is None:
124 if sqlite3 is None:
@@ -252,6 +261,9 b' class NotebookNotary(LoggingConfigurable):'
252 (datetime.utcnow(), self.algorithm, signature),
261 (datetime.utcnow(), self.algorithm, signature),
253 )
262 )
254 self.db.commit()
263 self.db.commit()
264 n, = self.db.execute("SELECT Count(*) FROM nbsignatures").fetchone()
265 if n > self.cache_size:
266 self.cull_db()
255
267
256 def unsign(self, nb):
268 def unsign(self, nb):
257 """Ensure that a notebook is untrusted
269 """Ensure that a notebook is untrusted
@@ -268,10 +280,11 b' class NotebookNotary(LoggingConfigurable):'
268 self.db.commit()
280 self.db.commit()
269
281
270 def cull_db(self):
282 def cull_db(self):
283 """Cull oldest 25% of the trusted signatures when the size limit is reached"""
271 self.db.execute("""DELETE FROM nbsignatures WHERE id IN (
284 self.db.execute("""DELETE FROM nbsignatures WHERE id IN (
272 SELECT id FROM nbsignatures ORDER BY last_seen DESC LIMIT -1 OFFSET ?
285 SELECT id FROM nbsignatures ORDER BY last_seen DESC LIMIT -1 OFFSET ?
273 );
286 );
274 """, (self.db_size_limit,))
287 """, (max(int(0.75 * self.cache_size), 1),))
275
288
276 def mark_cells(self, nb, trusted):
289 def mark_cells(self, nb, trusted):
277 """Mark cells as trusted if the notebook's signature can be verified
290 """Mark cells as trusted if the notebook's signature can be verified
@@ -18,7 +18,7 b' class TestNotary(TestsBase):'
18 self.notary = sign.NotebookNotary(
18 self.notary = sign.NotebookNotary(
19 secret=b'secret',
19 secret=b'secret',
20 profile_dir=get_ipython().profile_dir,
20 profile_dir=get_ipython().profile_dir,
21 db_url=':memory:'
21 db_file=':memory:'
22 )
22 )
23 with self.fopen(u'test3.ipynb', u'r') as f:
23 with self.fopen(u'test3.ipynb', u'r') as f:
24 self.nb = read(f, as_version=4)
24 self.nb = read(f, as_version=4)
@@ -64,41 +64,32 b' class TestNotary(TestsBase):'
64 # to ensure low resolution timestamps compare as expected
64 # to ensure low resolution timestamps compare as expected
65 dt = 2e-3
65 dt = 2e-3
66 nbs = [
66 nbs = [
67 copy.deepcopy(self.nb) for i in range(5)
67 copy.deepcopy(self.nb) for i in range(10)
68 ]
68 ]
69 for i, nb in enumerate(nbs):
69 for row in self.notary.db.execute("SELECT * FROM nbsignatures"):
70 print(row)
71 self.notary.cache_size = 8
72 for i, nb in enumerate(nbs[:8]):
70 nb.metadata.dirty = i
73 nb.metadata.dirty = i
71 self.notary.sign(nb)
74 self.notary.sign(nb)
72
75
73 for i, nb in enumerate(nbs):
76 for i, nb in enumerate(nbs[:8]):
74 time.sleep(dt)
77 time.sleep(dt)
75 self.assertTrue(self.notary.check_signature(nb), 'nb %i is trusted' % i)
78 self.assertTrue(self.notary.check_signature(nb), 'nb %i is trusted' % i)
76
79
77 self.notary.db_size_limit = 2
80 # signing the 9th triggers culling of first 3
78 self.notary.cull_db()
81 # (75% of 8 = 6, 9 - 6 = 3 culled)
79
82 self.notary.sign(nbs[8])
80 # expect all but last two signatures to be culled
83 self.assertFalse(self.notary.check_signature(nbs[0]))
81 self.assertEqual(
84 self.assertFalse(self.notary.check_signature(nbs[1]))
82 [self.notary.check_signature(nb) for nb in nbs],
85 self.assertFalse(self.notary.check_signature(nbs[2]))
83 [False] * (len(nbs) - 2) + [True] * 2
86 self.assertTrue(self.notary.check_signature(nbs[3]))
84 )
87 # checking nb3 should keep it from being culled:
85
88 self.notary.sign(nbs[0])
86 # sign them all again
89 self.notary.sign(nbs[1])
87 for nb in nbs:
90 self.notary.sign(nbs[2])
88 time.sleep(dt)
91 self.assertTrue(self.notary.check_signature(nbs[3]))
89 self.notary.sign(nb)
92 self.assertFalse(self.notary.check_signature(nbs[4]))
90
91 # checking front two marks them as newest for next cull instead of oldest
92 time.sleep(dt)
93 self.notary.check_signature(nbs[0])
94 self.notary.check_signature(nbs[1])
95 self.notary.cull_db()
96
97 self.assertEqual(
98 [self.notary.check_signature(nb) for nb in nbs],
99 [True] * 2 + [False] * (len(nbs) - 2)
100 )
101
102
93
103 def test_check_signature(self):
94 def test_check_signature(self):
104 nb = self.nb
95 nb = self.nb
General Comments 0
You need to be logged in to leave comments. Login now