Compare commits
3 Commits
33b01af78a
...
b056eb04ed
Author | SHA1 | Date | |
---|---|---|---|
b056eb04ed | |||
ee48d286c2 | |||
bde7828865 |
@ -12,28 +12,36 @@ class InvalidHexCode(Exception):
|
||||
|
||||
|
||||
class Not26Bit(InvalidHexCode):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Card number > 26 bits")
|
||||
def __init__(self, bits: bitstring.Bits) -> None:
|
||||
super().__init__(f"Card number > 26 bits [{bits.hex}]")
|
||||
|
||||
|
||||
class InvalidParity(InvalidHexCode):
|
||||
def __init__(self, even_odd: Literal["even", "odd"]) -> None:
|
||||
super().__init__(f"Bad {even_odd} parity")
|
||||
def __init__(self, bits: bitstring.Bits, even_odd: Literal["even", "odd"]) -> None:
|
||||
super().__init__(f"Bad {even_odd} parity [{bits.hex}]")
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Credential:
|
||||
bits: bitstring.Bits
|
||||
|
||||
@staticmethod
|
||||
def even_parity(bits: bitstring.Bits) -> bool:
|
||||
return bool(bits[7:19].count(1) % 2)
|
||||
|
||||
@staticmethod
|
||||
def odd_parity(bits: bitstring.Bits) -> bool:
|
||||
return (bits[19:31].count(1) % 2) == 0
|
||||
|
||||
@classmethod
|
||||
def from_code(cls, facility=int, card_number=int) -> "Credential":
|
||||
def from_code(cls, facility: int, card_number: int) -> "Credential":
|
||||
bits = bitstring.pack(
|
||||
"0b000000, 0b0, uint:8=facility, uint:16=card_number, 0b0",
|
||||
facility=facility,
|
||||
card_number=card_number,
|
||||
)
|
||||
bits[6] = bits[7:19].count(1) % 2 # even parity
|
||||
bits[31] = bits[19:31].count(0) % 2 # odd parity
|
||||
bits[6] = cls.even_parity(bits)
|
||||
bits[31] = cls.odd_parity(bits)
|
||||
return cls(bitstring.Bits(bits))
|
||||
|
||||
@classmethod
|
||||
@ -41,11 +49,11 @@ class Credential:
|
||||
bits = bitstring.Bits(hex=hex_code)
|
||||
|
||||
if bits[:6].any(1):
|
||||
raise Not26Bit
|
||||
if bits[6] != bits[7:19].count(1) % 2:
|
||||
raise InvalidParity("even")
|
||||
if bits[31] != (bits[19:31].count(0) % 2):
|
||||
raise InvalidParity("odd")
|
||||
raise Not26Bit(bits)
|
||||
if bits[6] != cls.even_parity(bits):
|
||||
raise InvalidParity(bits, "even")
|
||||
if bits[31] != (cls.odd_parity(bits)):
|
||||
raise InvalidParity(bits, "odd")
|
||||
|
||||
return cls(bits)
|
||||
|
||||
|
@ -18,3 +18,13 @@ class CredentialTestCase(TestCase):
|
||||
cred = Credential.from_code(facility_code, card_number)
|
||||
hex_cred = Credential.from_hex(cred.hex)
|
||||
self.assertEqual(cred, hex_cred)
|
||||
|
||||
def test_example_numbers(self):
|
||||
examples = [
|
||||
(100, 12345, "02C86073"),
|
||||
]
|
||||
|
||||
for facility_code, card_number, hex_number in examples:
|
||||
code_cred = Credential.from_code(facility_code, card_number)
|
||||
hex_cred = Credential.from_hex(hex_number)
|
||||
self.assertEqual(code_cred, hex_cred)
|
||||
|
@ -208,11 +208,14 @@ class DoorMember:
|
||||
|
||||
|
||||
def update_door(door: Door, dry_run: bool = False):
|
||||
logger.info(f"Updating {door}")
|
||||
logger.debug(f"Fetching members from database for {door}")
|
||||
members = [
|
||||
DoorMember.from_membershipworks_member(membershipworks_member, door)
|
||||
for membershipworks_member in (Member.objects.with_is_active()).all()
|
||||
]
|
||||
|
||||
logger.debug(f"Fetching cardholders from {door}")
|
||||
cardholders = {
|
||||
member.membershipworks_id: member
|
||||
for member in [
|
||||
@ -221,11 +224,13 @@ def update_door(door: Door, dry_run: bool = False):
|
||||
]
|
||||
}
|
||||
|
||||
logger.debug(f"Fetching credentials from {door}")
|
||||
existing_door_credentials = {
|
||||
Credential.from_hex(c.attrib["rawCardNumber"])
|
||||
for c in door.controller.get_credentials()
|
||||
}
|
||||
|
||||
logger.debug(f"Syncing members with {door}")
|
||||
# TODO: can I combine requests?
|
||||
for member in members:
|
||||
# cardholder did not exist, so add them
|
||||
|
Loading…
Reference in New Issue
Block a user