diff options
author | Tomek Dubrownik <t.dubrownik@gmail.com> | 2012-01-06 13:04:34 +0100 |
---|---|---|
committer | Tomek Dubrownik <t.dubrownik@gmail.com> | 2012-01-06 13:04:34 +0100 |
commit | 2bf714766cc0ae17b526d0de909f83f1b4c9031b (patch) | |
tree | 6149d42b6cfb672da596a4dd2063f63225306623 | |
parent | 52e7f09a8431694507191bc47da714c0850157c3 (diff) | |
download | doorman-2bf714766cc0ae17b526d0de909f83f1b4c9031b.tar.gz doorman-2bf714766cc0ae17b526d0de909f83f1b4c9031b.tar.bz2 doorman-2bf714766cc0ae17b526d0de909f83f1b4c9031b.tar.xz doorman-2bf714766cc0ae17b526d0de909f83f1b4c9031b.zip |
storage should be mostly fixed now
-rwxr-xr-x | admin/doorman_revoke_user | 10 | ||||
-rwxr-xr-x | admin/doorman_scan | 3 | ||||
-rw-r--r-- | admin/lib/actions.py | 5 | ||||
-rw-r--r-- | admin/lib/proto.py | 10 | ||||
-rw-r--r-- | admin/lib/storage/__init__.py | 2 | ||||
-rw-r--r-- | admin/lib/storage/classes.py | 55 | ||||
-rw-r--r-- | admin/lib/storage/ops.py | 37 | ||||
-rw-r--r-- | admin/lib/storage/storage_encapsulation.py | 136 |
8 files changed, 250 insertions, 8 deletions
diff --git a/admin/doorman_revoke_user b/admin/doorman_revoke_user index 87c782a..af87735 100755 --- a/admin/doorman_revoke_user +++ b/admin/doorman_revoke_user @@ -14,12 +14,12 @@ if __name__ == '__main__': proto = Proto(url) user = argv[1] cards = cards_for_user(user) - for mid in cards: - print 'Revoking card %x' % mid - status = revoke_mid(token, mid, proto=proto) + for h in cards: + print 'Revoking card %s' % h + status = revoke_hash(token, h, proto=proto) if status.command == 'K': - del_card(mid) - print 'Card %x revoked' % mid + del_card(h) + print 'Card %s revoked' % h else: print >> stderr, 'Unknown error:', status exit(1) diff --git a/admin/doorman_scan b/admin/doorman_scan index 9919f83..6e6bf34 100755 --- a/admin/doorman_scan +++ b/admin/doorman_scan @@ -6,9 +6,10 @@ from lib.actions import scan from lib.proto import Proto from lib.storage import get_card from lib.password import get_token +import options if __name__ == '__main__': - url = argv[1] if len(argv) > 1 else None + url = argv[1] if len(argv) > 1 else options.url token = get_token() proto = Proto(url) print 'Please swipe token' diff --git a/admin/lib/actions.py b/admin/lib/actions.py index 12f45a3..44f13c8 100644 --- a/admin/lib/actions.py +++ b/admin/lib/actions.py @@ -1,9 +1,10 @@ from proto import Proto +from options import * from command import Command def scan(token, url=None, proto=None): proto = proto or Proto(url) - proto.send(Command(command='G', uid=0, hash=0, token=token)) + proto.send(Command(command='G', uid=0, hash=empty_hash, token=token)) return proto.recv() def add(token, hash, uid=0, url=None, proto=None): @@ -13,7 +14,7 @@ def add(token, hash, uid=0, url=None, proto=None): def revoke_uid(token, uid, url=None, proto=None): proto = proto or Proto(url) - proto.send(Command(command='R', hash=0, uid=uid, token=token)) + proto.send(Command(command='R', hash=empty_hash, uid=uid, token=token)) return proto.recv() def revoke_hash(token, hash, url=None, proto=None): diff --git a/admin/lib/proto.py b/admin/lib/proto.py index b74af1b..025dc99 100644 --- a/admin/lib/proto.py +++ b/admin/lib/proto.py @@ -21,10 +21,20 @@ class Proto(object): print >> stderr, 'Serial port ready' def send(self, command): cmd = str(command) + '\n' + print cmd self.fd.write(cmd) def recv(self): line = self.fd.readline() + print line cmd = Command.from_str(line) if cmd.command == 'E': raise RemoteException(cmd.hash, cmd.uid) return cmd + +class MockProto(object): + def __init__(*a, **kw): + pass + def send(self, command): + pass + def recv(self): + return Command() diff --git a/admin/lib/storage/__init__.py b/admin/lib/storage/__init__.py new file mode 100644 index 0000000..d074522 --- /dev/null +++ b/admin/lib/storage/__init__.py @@ -0,0 +1,2 @@ +from classes import CsvStorage, JsonStorage +from ops import * diff --git a/admin/lib/storage/classes.py b/admin/lib/storage/classes.py new file mode 100644 index 0000000..e6a93fe --- /dev/null +++ b/admin/lib/storage/classes.py @@ -0,0 +1,55 @@ +from sys import stderr +from collections import MutableMapping +from StringIO import StringIO + +import csv, json + +class Storage(MutableMapping): + def __init__(self, encapsulation): + self.encapsulation = encapsulation + self.encapsulation.begin_transaction() + self.data = self.decode(encapsulation.data) + def sync(self): + self.encapsulation.data = self.encode(self.data) + self.encapsulation.end_transaction() + + self.encapsulation.begin_transaction() + def __setitem__(self, k, v): + self.data[k] = v + self.sync() + def __delitem__(self, k): + del self.data[k] + self.sync() + def __getitem__(self, k): + return self.data[k] + def __len__(self, k): + return len(self.data) + def __iter__(self): + return iter(self.data) + +class CsvStorage(Storage): + def decode(self, text): + try: + stored = {x[0]: [x[1], x[2]] + for x in csv.reader(StringIO(text))} + except IOError as e: + print >>stderr, e + stored = {} + return stored + def encode(self, data): + f = StringIO() + csv.writer(f).writerows( + [c, u, name] for c, (u, name) in data.iteritems()) + return f.getvalue() + +class JsonStorage(Storage): + def decode(self, text): + try: + stored = json.loads(self.encapsulation.data) + except IOError as e: + print >>stderr, e + stored = {} + return stored + def encode(self, data): + return json.dumps(data, indent=4) + diff --git a/admin/lib/storage/ops.py b/admin/lib/storage/ops.py new file mode 100644 index 0000000..411c810 --- /dev/null +++ b/admin/lib/storage/ops.py @@ -0,0 +1,37 @@ +import options +import storage_encapsulation +from .classes import CsvStorage, JsonStorage + +if options.storage_encrypt == True: + encapsulation_class = storage_encapsulation.DESFileEncapsulation +else: + encapsulation_class = storage_encapsulation.RawFileEncapsulation + +if options.storage == 'json': + encapsulation = encapsulation_class(options.json) + storage = JsonStorage(encapsulation) +if options.storage == 'csv': + encapsulation = encapsulation_class(options.csv) + storage = CsvStorage(encapsulation) + +nobody = (None, '-unknown-') + +get_card = lambda h: storage.get(h, nobody) +cards_for_user = lambda name: map(lambda (k,v): k, + filter(lambda (k,(u,n)): n == name, storage.iteritems())) + +def add_user(username, hash, uid): + storage[hash] = (uid, username) + +def del_card(hash): + return storage.pop(hash, nobody) + +def del_filter(f): + cards = map(lambda (k,v): k, filter(f, storage.iteritems())) + r = [] + for c in cards: + r.append(storage.pop(c, nobody)) + return r + +del_uid = lambda uid: del_filter(lambda (k, (u,n)): u == uid) +del_username = lambda name: del_filter(lambda (k, (u,n)): n == name) diff --git a/admin/lib/storage/storage_encapsulation.py b/admin/lib/storage/storage_encapsulation.py new file mode 100644 index 0000000..6c37692 --- /dev/null +++ b/admin/lib/storage/storage_encapsulation.py @@ -0,0 +1,136 @@ +# because admin.lib.password uses admin.options -_- +if __name__ == "__main__": + import sys + sys.path.append("d:\\Development\\Projects\\doorman\\admin") + +import hashlib +import os +import tempfile + +import lib.password + +class RawFileEncapsulation(object): + """ + Encapsulates data in a file. With style! + + The data is available in .data. + """ + + def __init__(self, filename): + self.original_filename = filename + self.original_checksum = None + self.storage = None + + def _decode_data(self, data): + """ + Override me. + """ + return data + + def _encode_data(self, data): + """ + Override me. + """ + return data + + def _backup_file_path(self, original_path): + """ + Override me if you care. + """ + return original_path + "_OHSHITSOMETHINGWENTWRONG" + + def begin_transaction(self): + """ + Creates a temporary file that we'll use before writing all. + """ + + with open(self.original_filename, "rb") as original: + self.data = self._decode_data(original.read()) + self.original_checksum = hashlib.sha1(self.data).hexdigest() + + def end_transaction(self): + """ + Verifies everything went well and commits changes to the file. + """ + + original = open(self.original_filename, "rb") + verification_data = self._decode_data(original.read()) + verification_checksum = hashlib.sha1(verification_data).hexdigest() + + if verification_checksum != self.original_checksum: + #TODO: Implement separate exception + raise Exception("File changed since we last opened it!") + + # Let's make a backup of the old original... Because we're paranoid. + # We're writing the encapsulated versiuon here so that it can be a + # drop-in replacement. + backup_filename = self._backup_file_path(self.original_filename) + with open(backup_filename, "wb") as backup: + backup.write(verification_data) + + # BIG TODO: Fix the race condition that occurs here: the file can be + # opened by another program here and it can be modified! + # Oh well. + + original = open(self.original_filename, "wb") + original.write(self._encode_data(self.data)) + + # Remove the backup. + os.remove(backup_filename) + +class DESFileEncapsulation(RawFileEncapsulation): + MAGIC = "it's a kind of magic!" + def __init__(self, filename, magic_check=True): + try: + import Crypto + except: + raise Exception("PyCrypto not installed!") + + self.des_key = password.get_des_storage_key(filename) + self.magic_check = magic_check + super(DESFileEncapsulation, self).__init__(filename) + + def _decode_data(self, data): + if data == "": + print "Input file empty. Assuming actually empty file." + return "" + + from Crypto.Cipher import DES + des = DES.new(self.des_key, DES.MODE_CBC) + data_padded = des.decrypt(data) + + padding_length = ord(data_padded[-1]) + data_unpadded = data_padded[:-padding_length] + + if self.magic_check and not data_unpadded.startswith(self.MAGIC): + raise Exception("Bad magic! Did you mistype a key?") + + return data_unpadded[len(self.MAGIC):] + + def _encode_data(self, data): + from Crypto.Cipher import DES + des = DES.new(self.des_key, DES.MODE_CBC) + + if self.magic_check: + data = self.MAGIC + data + padding_length = 8 - len(data) % 8 + data_padded = data + chr(padding_length) * padding_length + + return des.encrypt(data_padded) + +if __name__ == "__main__": + r = DESFileEncapsulation("d:/dupa.txt") + r.begin_transaction() + + try: + n = int(r.data) + except: + n = 0 + + print "ass! %i" % n + r.data = str(n + 1) + + print "try to modify the assfile, see it fail!" + raw_input() + + r.end_transaction() |