Skip to content

Conversation

@rechner
Copy link
Member

@rechner rechner commented Oct 17, 2025

Currently, all access control decisions at Pawprint are being made using LDAP as our source of truth. Keeping LDAP users synced and turning off access after a user's payment has lapsed is a manual process.

MemberMatters has direct support for access control using a websocket protocol, this PR adds support for emulating a MemberMatters access control device to Doorman.

Doorman will first consult the access list from Member Matters, and can optionally fall-back to querying a card in LDAP.

The access list from MemberMatters is cached locally in the file specified by DOORMAN_MM_DATA_FILE, so even for our instance hosted in the cloud, the doorman instance running locally will continue to work during an internet outage.

Copy link
Member

@kataze kataze left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lookin' hot 🔥

I think this will help remove a lot of maintenance overhead now that people are pausing and restarting their memberships all the time. Couple thoughts, nothing crazy.

Comment on lines +61 to +82
def _lookup_mm(card_number: str) -> bool:
with open(MM_DATA_FILE) as f:
mm_data = json.load(f)
authorized_tags = mm_data.get("tags", [])
locked_out = mm_data.get("locked_out", False)
if locked_out:
app.logger.info(
"This MemberMatters device was set to locked_out by the server!"
)
return False

app.logger.debug(
f"Loaded {len(authorized_tags)} tags from MemberMatters cache at {MM_DATA_FILE}"
)
if card_number in authorized_tags:
app.logger.info(f"card_number: {card_number} is authorized by MemberMatters")
return True

app.logger.debug(f"card_number: {card_number} not authorized by MemberMatters")
return False


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _lookup_mm(card_number: str) -> bool:
with open(MM_DATA_FILE) as f:
mm_data = json.load(f)
authorized_tags = mm_data.get("tags", [])
locked_out = mm_data.get("locked_out", False)
if locked_out:
app.logger.info(
"This MemberMatters device was set to locked_out by the server!"
)
return False
app.logger.debug(
f"Loaded {len(authorized_tags)} tags from MemberMatters cache at {MM_DATA_FILE}"
)
if card_number in authorized_tags:
app.logger.info(f"card_number: {card_number} is authorized by MemberMatters")
return True
app.logger.debug(f"card_number: {card_number} not authorized by MemberMatters")
return False
def _lookup_mm(card_number: str) -> bool:
with open(MM_DATA_FILE) as f:
mm_data = json.load(f)
app.logger.debug(
f"Loaded {len(authorized_tags)} tags from MemberMatters cache at {MM_DATA_FILE}"
)
authorized_tags = mm_data.get("tags", [])
locked_out = mm_data.get("locked_out", False)
if locked_out:
app.logger.info(
"This MemberMatters device was set to locked_out by the server!"
)
return False
if card_number in authorized_tags:
app.logger.info(f"card_number: {card_number} is authorized by MemberMatters")
return True
app.logger.debug(f"card_number: {card_number} not authorized by MemberMatters")
return False

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just trying to move the loaded from cache message but the diff looks awful. Maybe I screwed up some whitespace in this plaintext comment editor lol

Comment on lines +115 to +116
if not ret:
ret = _lookup_ldap(card_number)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this fallback authorize people even if the device is "locked_out"? Is that what we want?

Comment on lines +87 to +88
# Not implemented (lol)
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

# Not implemented (lol)
pass

elif command == "bump":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would "open" be more clear? Or is it reserved somehow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants