]> granicus.if.org Git - apache/blob - support/ctlogconfig
* Doing a PROPFIND on a large collection e.g. 50.000 elements can easily
[apache] / support / ctlogconfig
1 #!/usr/bin/env python
2 #
3 # Licensed to the Apache Software Foundation (ASF) under one or more
4 # contributor license agreements.  See the NOTICE file distributed with
5 # this work for additional information regarding copyright ownership.
6 # The ASF licenses this file to You under the Apache License, Version 2.0
7 # (the "License"); you may not use this file except in compliance with
8 # the License.  You may obtain a copy of the License at
9 #
10 #     http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17
18 import os
19 import re
20 import sqlite3
21 import sys
22
23
24 def create_tables(db_name):
25     cxn = sqlite3.connect(db_name)
26     cur = cxn.cursor()
27
28     cur.execute(
29         'CREATE TABLE loginfo('
30         + 'id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
31         + 'log_id TEXT, '
32         + 'public_key TEXT, '  # path to PEM-encoded file
33         + 'distrusted INTEGER, '  # non-zero if not trusted
34         + 'min_valid_timestamp INTEGER, '
35         + 'max_valid_timestamp INTEGER, '
36         + 'url TEXT)'
37     )
38     cur.close()
39     cxn.commit()
40     cxn.close()
41
42
43 def record_id_arg(cur, args, required=False):
44     if len(args) < 1 or args[0][0] != '#' or len(args[0]) < 2:
45         if required:
46             print >> sys.stderr, 'A record id was not provided'
47             sys.exit(1)
48         return None
49     record_id = args.pop(0)[1:]
50     stmt = 'SELECT * FROM loginfo WHERE id = ?'
51     cur.execute(stmt, [record_id])
52     recs = list(cur.fetchall())
53     assert len(recs) < 2
54     if len(recs) == 0:
55         print >> sys.stderr, 'Record #%s was not found' % record_id
56         sys.exit(1)
57     return record_id
58
59
60 def log_id_arg(cur, args, required=True):
61     if len(args) < 1 or len(args[0]) != 64:
62         if not required:
63             return None
64         print >> sys.stderr, 'A log id was not provided'
65         sys.exit(1)
66     log_id = args.pop(0).upper()
67     if len(re.compile(r'[A-Z0-9]').findall(log_id)) != len(log_id):
68         print >> sys.stderr, 'The log id is not formatted properly'
69         sys.exit(1)
70     return log_id
71
72
73 def public_key_arg(args):
74     if len(args) < 1:
75         print >> sys.stderr, 'A public key file was not provided'
76         sys.exit(1)
77     pubkey = args.pop(0)
78     if not os.path.exists(pubkey):
79         print >> sys.stderr, 'Public key file %s could not be read' % pubkey
80         sys.exit(1)
81     return pubkey
82
83
84 def time_arg(args):
85     if len(args) < 1:
86         print >> sys.stderr, 'A timestamp was not provided'
87         sys.exit(1)
88     t = args.pop(0)
89     if t == '-':
90         return None
91     bad_val = False
92     val = None
93     try:
94         val = int(t)
95     except ValueError:
96         bad_val = True
97
98     if bad_val or val < 1:
99         print >> sys.stderr, 'The timestamp "%s" is invalid' % t
100         sys.exit(1)
101
102     return val
103
104
105 def configure_public_key(cur, args):
106     record_id = record_id_arg(cur, args, False)
107     public_key = public_key_arg(args)
108     if len(args) != 0:
109         usage()
110     if not record_id:
111         stmt = 'INSERT INTO loginfo (public_key) VALUES(?)'
112         cur.execute(stmt, [public_key])
113     else:
114         stmt = 'UPDATE loginfo SET public_key = ? WHERE id = ?'
115         cur.execute(stmt, [public_key, record_id])
116
117
118 def configure_url(cur, args):
119     # can't specify more than one of record-id and log-id
120     log_id = None
121     record_id = record_id_arg(cur, args, False)
122     if not record_id:
123         log_id = log_id_arg(cur, args, False)
124     if len(args) != 1:
125         usage()
126     url = args.pop(0)
127
128     if record_id:
129         stmt = 'UPDATE loginfo SET url = ? WHERE id = ?'
130         args = [url, record_id]
131     elif log_id:
132         stmt = 'INSERT INTO loginfo (log_id, url) VALUES(?, ?)'
133         args = [log_id, url]
134     else:
135         stmt = 'INSERT INTO loginfo (url) VALUES(?)'
136         args = [url]
137
138     cur.execute(stmt, args)
139
140
141 def forget_log(cur, args):
142     record_id = record_id_arg(cur, args, False)
143     log_id = None
144     if not record_id:
145         log_id = log_id_arg(cur, args, True)
146     if len(args) != 0:
147         usage()
148     if record_id:
149         stmt = 'DELETE FROM loginfo WHERE id = ?'
150         args = [record_id]
151     else:
152         stmt = 'DELETE FROM loginfo WHERE log_id = ?'
153         args = [log_id]
154     cur.execute(stmt, args)
155
156
157 def trust_distrust_log(cur, args):
158     # could take a record id or a log id
159     record_id = record_id_arg(cur, args, False)
160     if record_id:
161         log_id = None
162     else:
163         log_id = log_id_arg(cur, args)
164
165     if len(args) != 1:
166         usage()
167     flag = args.pop(0)
168
169     if not record_id:
170         stmt = 'INSERT INTO loginfo (log_id, distrusted) VALUES(?, ?)'
171         cur.execute(stmt, [log_id, flag])
172     else:
173         stmt = 'UPDATE loginfo SET distrusted = ? WHERE id = ?'
174         cur.execute(stmt, [flag, record_id])
175
176
177 def trust_log(cur, args):
178     trust_distrust_log(cur, args + [0])
179
180
181 def distrust_log(cur, args):
182     trust_distrust_log(cur, args + [1])
183
184
185 def time_range(cur, args):
186     # could take a record id or a log id
187     record_id = record_id_arg(cur, args, False)
188     if record_id:
189         log_id = None
190     else:
191         log_id = log_id_arg(cur, args)
192
193     min_valid_time = time_arg(args)
194     max_valid_time = time_arg(args)
195     if len(args) != 0:
196         usage()
197     if not record_id:
198         stmt = 'INSERT INTO loginfo ' + \
199                '(log_id, min_valid_timestamp, max_valid_timestamp) ' + \
200                'VALUES(?, ?, ?)'
201         cur.execute(stmt, [log_id, min_valid_time, max_valid_time])
202     else:
203         stmt = 'UPDATE loginfo SET min_valid_timestamp = ?, ' + \
204                'max_valid_timestamp = ? WHERE id = ?'
205         cur.execute(stmt, [min_valid_time, max_valid_time, record_id])
206
207
208 class ConfigEntry:
209
210     pass
211
212
213 def dump_ll(cur):
214     stmt = 'SELECT * FROM loginfo'
215     cur.execute(stmt)
216     recs = []
217     for row in cur.fetchall():
218         obj = ConfigEntry()
219         obj.id = row[0]
220         obj.log_id = row[1]
221         obj.public_key = row[2]
222         obj.distrusted = row[3]
223         obj.min_valid_timestamp = row[4]
224         obj.max_valid_timestamp = row[5]
225         obj.url = row[6]
226         recs += [obj]
227     return recs
228
229
230 def dump(cur, args):
231     if len(args) != 0:
232         usage()
233     recs = dump_ll(cur)
234     for rec in recs:
235         not_conf = '(not configured)'
236
237         mint = \
238             str(rec.min_valid_timestamp) if rec.min_valid_timestamp else '-INF'
239         maxt = \
240             str(rec.max_valid_timestamp) if rec.max_valid_timestamp else '+INF'
241         print 'Log entry:'
242         print '  Record ' + str(rec.id) + \
243             (' (DISTRUSTED)' if rec.distrusted else '')
244         print '  Log id         : ' + (rec.log_id if rec.log_id else not_conf)
245         print '  Public key file: ' + \
246             (rec.public_key if rec.public_key else not_conf)
247         print '  URL            : ' + (rec.url if rec.url else not_conf)
248         print '  Time range     : ' + mint + ' to ' + maxt
249         print ''
250
251
252 def usage():
253     help = """Usage: %s /path/to/log-config-db command args
254
255 Commands:
256   display config-db contents:
257     dump
258   configure public key:
259     configure-public-key [log-id|record-id] /path/log-pub-key.pem
260   configure URL:
261     configure-url [log-id|record-id] http://www.example.com/path/
262   configure min and/or max valid timestamps:
263     valid-time-range log-id|record-id min-range max-range
264   mark log as trusted (default):
265     trust log-id|record-id
266   mark log as untrusted:
267     distrust log-id|record-id
268   remove log config from config-db:
269     forget log-id|record-id
270
271 log-id is a 64-character hex string representation of a log id
272
273 record-id references an existing entry and is in the form:
274   #<record-number>
275   (displayable with the dump command)
276 """ % sys.argv[0]
277     print >> sys.stderr, help
278     sys.exit(1)
279
280
281 def main(argv):
282     if len(argv) < 3:
283         usage()
284
285     db_name = argv[1]
286     cmd = argv[2]
287     args = argv[3:]
288
289     cmds = {'configure-public-key': configure_public_key,
290             'configure-url': configure_url,
291             'distrust': distrust_log,
292             'trust': trust_log,
293             'forget': forget_log,
294             'valid-time-range': time_range,
295             'dump': dump,
296             }
297
298     cmds_requiring_db = ['dump', 'forget']  # db must already exist
299
300     if not cmd in cmds:
301         usage()
302
303     if not os.path.exists(db_name):
304         if not cmd in cmds_requiring_db:
305             create_tables(db_name)
306         else:
307             print >> sys.stderr, 'Database "%s" does not exist' % db_name
308             sys.exit(1)
309
310     cxn = sqlite3.connect(db_name)
311     cur = cxn.cursor()
312
313     cmds[cmd](cur, args)
314
315     cur.close()
316     cxn.commit()
317     cxn.close()
318
319 if __name__ == "__main__":
320     main(sys.argv)