summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMateusz Maciaś <ursereg@gmail.com>2012-01-06 13:12:13 +0100
committerMateusz Maciaś <ursereg@gmail.com>2012-01-06 13:12:13 +0100
commit6d2b9d3dc6f52393f337f107e67d334f810b764c (patch)
tree34e1110add9593641f85e1d1777bc33ea619a37f
parentd8a4d06852c23f83054aa5efff6fb8fd0acb5f91 (diff)
parent2bf714766cc0ae17b526d0de909f83f1b4c9031b (diff)
downloaddoorman-6d2b9d3dc6f52393f337f107e67d334f810b764c.tar.gz
doorman-6d2b9d3dc6f52393f337f107e67d334f810b764c.tar.bz2
doorman-6d2b9d3dc6f52393f337f107e67d334f810b764c.tar.xz
doorman-6d2b9d3dc6f52393f337f107e67d334f810b764c.zip
Merge branch 'minefield' of github.com:tdubrownik/doorman into minefield
-rwxr-xr-xadmin/doorman_revoke_user10
-rwxr-xr-xadmin/doorman_scan3
-rw-r--r--admin/lib/actions.py5
-rw-r--r--admin/lib/proto.py10
-rw-r--r--admin/lib/storage/__init__.py2
-rw-r--r--admin/lib/storage/classes.py55
-rw-r--r--admin/lib/storage/ops.py37
-rw-r--r--admin/lib/storage/storage_encapsulation.py136
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()