329 lines
4.4 KiB
Markdown
329 lines
4.4 KiB
Markdown
CA/PKI Backend Project Context (Updated)
|
|
Stack
|
|
|
|
Python 3 + psycopg (dict_row cursors)
|
|
|
|
PostgreSQL database: ca
|
|
|
|
Unit tests: unittest
|
|
|
|
Run via: python3 -m unittest discover
|
|
|
|
Current test count: 17
|
|
|
|
Database Schema (current assumptions)
|
|
entity
|
|
|
|
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
|
|
|
|
creation_ts TIMESTAMPTZ DEFAULT now()
|
|
|
|
creator INT FK → entity(id) (nullable)
|
|
|
|
name VARCHAR(100) NOT NULL
|
|
|
|
type VARCHAR(...) NOT NULL
|
|
|
|
Allowed types: person, group, device
|
|
|
|
public_key VARCHAR(300) NOT NULL
|
|
|
|
symmetrical_key VARCHAR(100) NULL
|
|
|
|
status VARCHAR(...) NOT NULL DEFAULT 'active'
|
|
|
|
Values: 'active', 'revoked'
|
|
|
|
expiration DATE NULL
|
|
|
|
ca_reference VARCHAR(100) NULL
|
|
|
|
Constraint
|
|
CHECK (
|
|
(type = 'group' AND ca_reference IS NOT NULL)
|
|
OR
|
|
(type <> 'group' AND ca_reference IS NULL)
|
|
)
|
|
|
|
Rule:
|
|
|
|
Groups MUST have a ca_reference
|
|
|
|
All other entity types MUST have ca_reference IS NULL
|
|
|
|
Indexes:
|
|
|
|
Index on entity(name)
|
|
|
|
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)
|
|
|
|
PRIMARY KEY (group_id, member_id)
|
|
|
|
Index (member_id, group_id)
|
|
|
|
Groups can contain:
|
|
|
|
persons
|
|
|
|
devices
|
|
|
|
other groups
|
|
|
|
property
|
|
|
|
id INT FK → entity(id) ON DELETE CASCADE
|
|
|
|
property_name VARCHAR(100) NOT NULL
|
|
|
|
validation_policy CHAR(19) NOT NULL DEFAULT 'default'
|
|
|
|
source VARCHAR(150) NULL
|
|
|
|
PRIMARY KEY (id, property_name)
|
|
|
|
Notes:
|
|
|
|
validation_policy is CHAR(19) and padded by PostgreSQL.
|
|
|
|
Used for flags/roles such as "creator".
|
|
|
|
metadata
|
|
|
|
Singleton row table (enforced at application level).
|
|
|
|
Columns:
|
|
|
|
name
|
|
|
|
comment
|
|
|
|
private_key
|
|
|
|
public_key
|
|
|
|
defense_p BOOLEAN NOT NULL DEFAULT false
|
|
|
|
defense_p:
|
|
|
|
Global system flag.
|
|
|
|
Logged on change.
|
|
|
|
log
|
|
|
|
id SERIAL PRIMARY KEY
|
|
|
|
ts TIMESTAMPTZ DEFAULT now()
|
|
|
|
entry TEXT NOT NULL
|
|
|
|
Every API mutation must insert exactly one row here.
|
|
|
|
Core Business Rules
|
|
Creators are NOT an entity type
|
|
|
|
"creator" is a property (property_name='creator') on a person.
|
|
|
|
insert_creator():
|
|
|
|
Creates a person
|
|
|
|
Adds "creator" property
|
|
|
|
Revoked entities are immutable
|
|
|
|
All entity mutations must call:
|
|
|
|
ensure_entity_active(cursor, entity_id)
|
|
|
|
Revoked entities CANNOT:
|
|
|
|
Join groups
|
|
|
|
Accept members
|
|
|
|
Add/delete properties
|
|
|
|
Change public_key
|
|
|
|
Change symmetrical_key
|
|
|
|
Change status again
|
|
|
|
Group CA Reference Rule
|
|
|
|
group entities must include a non-null ca_reference.
|
|
|
|
person and device must not define ca_reference.
|
|
|
|
Enforced at:
|
|
|
|
Database level (CHECK constraint)
|
|
|
|
Python validation level
|
|
|
|
Property Metadata
|
|
|
|
Each property includes:
|
|
|
|
validation_policy
|
|
|
|
source
|
|
|
|
Defaults:
|
|
|
|
validation_policy = 'default'
|
|
|
|
source = NULL
|
|
|
|
Property mutations:
|
|
|
|
Require entity to be active
|
|
|
|
Must log changes
|
|
|
|
Metadata Defense Flag
|
|
|
|
defense_p:
|
|
|
|
Boolean system-wide flag
|
|
|
|
Default: false
|
|
|
|
Must be logged when changed
|
|
|
|
Logging Rules
|
|
|
|
All changes to:
|
|
|
|
entity
|
|
|
|
group_member
|
|
|
|
property
|
|
|
|
metadata
|
|
|
|
Must call:
|
|
|
|
log_change(cursor, "...")
|
|
|
|
Logging:
|
|
|
|
Happens inside same transaction
|
|
|
|
No extra commits
|
|
|
|
Exactly one log row per mutation
|
|
|
|
Python Modules
|
|
ca_core/entity.py
|
|
|
|
Provides:
|
|
|
|
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, ca_reference)
|
|
|
|
get_entity(cursor, entity_id)
|
|
|
|
set_entity_status(cursor, entity_id, status, changed_by)
|
|
|
|
set_entity_keys(cursor, entity_id, public_key, changed_by)
|
|
|
|
set_symmetrical_key(cursor, entity_id, key, changed_by)
|
|
|
|
get_symmetrical_key(cursor, entity_id)
|
|
|
|
ca_core/group_member.py
|
|
|
|
Uses member_id
|
|
|
|
Prevents adding revoked groups/members
|
|
|
|
Logs membership add/remove
|
|
|
|
ca_core/property.py
|
|
|
|
Table:
|
|
|
|
property(id, property_name, validation_policy, source)
|
|
|
|
Rules:
|
|
|
|
Reject mutations if entity revoked
|
|
|
|
Logs set/delete
|
|
|
|
Default policy 'default'
|
|
|
|
validation_policy is CHAR(19)
|
|
|
|
ca_core/metadata.py
|
|
|
|
Updates metadata fields
|
|
|
|
Manages defense_p
|
|
|
|
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:
|
|
|
|
Creation and enrollment
|
|
|
|
Group membership
|
|
|
|
Revocation immutability
|
|
|
|
CA reference enforcement
|
|
|
|
Property metadata fields
|
|
|
|
defense_p behavior
|
|
|
|
Log entry creation (case-insensitive substring checks)
|
|
|
|
Run via:
|
|
|
|
python3 -m unittest discover
|
|
Known Gotchas
|
|
|
|
Do NOT name module logging.py (conflicts with stdlib)
|
|
|
|
Schema and code must stay aligned:
|
|
|
|
property.id (NOT entity_id)
|
|
|
|
group_member.member_id
|
|
|
|
entity.ca_reference constraint
|
|
|
|
CHAR(19) pads values with spaces
|