168 lines
3.4 KiB
Markdown
168 lines
3.4 KiB
Markdown
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).
|