3.4 KiB
CA/PKI Backend Project Context Stack
Python 3 + psycopg (dict_row cursors)
PostgreSQL database: ca
Unit tests: unittest (python3 -m unittest discover)
Database Schema (current assumptions) entity
id INT identity PK
creation_ts TIMESTAMPTZ default now()
creator INT FK → entity(id) (the entity that created this one; nullable)
name VARCHAR(100) NOT NULL
type VARCHAR(...) NOT NULL (e.g. person, group, device, alias)
public_key VARCHAR(300) NOT NULL
symmetrical_key VARCHAR(100) NULL
status VARCHAR(...) NOT NULL default 'active' (values: 'active', 'revoked')
expiration DATE NULL
Index on entity(name) (and other indexes as needed)
group_member
group_id INT FK → entity(id) ON DELETE CASCADE
member_id INT FK → entity(id) ON DELETE CASCADE
role VARCHAR(10)
PK (group_id, member_id)
Index (member_id, group_id)
Groups can contain any entity type, including other groups and devices.
property
Columns: (id INT FK → entity(id), property_name VARCHAR(100))
PK (id, property_name)
Used for flags/roles such as "creator"
metadata
Intended “singleton row” table, enforced at application level
Columns: name, comment, private_key, public_key
log
id SERIAL PK
ts TIMESTAMPTZ default now()
entry TEXT NOT NULL
Every API mutation must log one row here.
Core Business Rules
Creators are not an entity type
creator is a property (property_name='creator') on a person entity.
insert_creator() creates a person entity and inserts the creator property.
Revoked entities are immutable
Any mutation on an entity requires ensure_entity_active(cursor, entity_id).
Revoked entities cannot:
Join groups or accept members
Add/delete properties
Change keys (public_key, symmetrical_key)
Change status again
Logging
All changes to entity, group_member, property, metadata must call log_change(cursor, "...")
Logging happens inside the same transaction (no extra commits).
Python Modules (current structure)
ca_core/entity.py
Must provide:
ensure_entity_active(cursor, entity_id)
insert_creator(cursor, name, public_key)
enroll_person(cursor, name, public_key, creator_id)
create_group(cursor, name, public_key, creator_id)
create_alias(cursor, target_entity_id)
get_entity(cursor, entity_id)
set_entity_status(cursor, entity_id, status, changed_by) (requires active entity)
set_entity_keys(cursor, entity_id, public_key, changed_by) (active-only)
set_symmetrical_key(cursor, entity_id, key, changed_by) (active-only)
get_symmetrical_key(cursor, entity_id)
ca_core/group_member.py
Uses member_id (not person_id)
Must prevent adding revoked groups/members (via ensure_entity_active)
Logs add/remove membership
ca_core/property.py
Table is property(id, property_name) (NOT entity_id/name)
Must reject mutations if entity revoked (immutability)
Logs set/delete property
ca_core/metadata.py
Updates metadata fields and logs changes
ca_core/db_logging.py
log_change(cursor, message: str) inserts into log(entry)
Tests
tests/test_entity.py, tests/test_group.py, tests/test_property.py, tests/test_metadata.py
Tests verify:
Core behaviors (create, enroll, group membership, revoke immutability)
Log entry is created for mutations (case-insensitive substring checks)
Run via:
python3 -m unittest discover
Known gotchas
Avoid naming a module logging.py (conflicts with stdlib). Use db_logging.py.
Schema and code must stay aligned (e.g., property.id/property_name, group_member.member_id).