pki_ca/PROJECT_CONTEXT.md

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).