From d645a143e3faea6eb2a4db37cc12f85158549bdc Mon Sep 17 00:00:00 2001 From: Mick Letofsky Date: Tue, 27 Jan 2026 13:06:22 +0100 Subject: [PATCH] Claude & ReadMe markdown improvements --- util/Seeder/{.claude => }/CLAUDE.md | 78 ++++++++++------ util/Seeder/README.md | 140 ++++++++++++++++++++++------ 2 files changed, 160 insertions(+), 58 deletions(-) rename util/Seeder/{.claude => }/CLAUDE.md (69%) diff --git a/util/Seeder/.claude/CLAUDE.md b/util/Seeder/CLAUDE.md similarity index 69% rename from util/Seeder/.claude/CLAUDE.md rename to util/Seeder/CLAUDE.md index 2e75494662..d7f63285c0 100644 --- a/util/Seeder/.claude/CLAUDE.md +++ b/util/Seeder/CLAUDE.md @@ -1,5 +1,51 @@ # Seeder - Claude Code Context +## Ubiquitous Language + +The Seeder follows six core patterns: + +1. **Factories** - Create ONE entity with encryption. Named `{Entity}Seeder` with `Create{Type}{Entity}()` methods. Do not interact with database. + +2. **Recipes** - Orchestrate MANY entities. Named `{DomainConcept}Recipe`. **MUST have `Seed()` method** as primary interface, not `AddToOrganization()` or similar. Use parameters for variations, not separate methods. Compose Factories internally. + +3. **Models** - DTOs bridging SDK ↔ Server format. Named `{Entity}ViewDto` (plaintext), `Encrypted{Entity}Dto` (SDK format). Pure data, no logic. + +4. **Scenes** - Complete test scenarios with ID mangling. Implement `IScene`. Async, returns `SceneResult` with MangleMap. Named `{Scenario}Scene`. + +5. **Queries** - Read-only data retrieval. Implement `IQuery`. Synchronous, no DB modifications. Named `{DataToRetrieve}Query`. + +6. **Data** - Static, filterable test data collections (Companies, Passwords, Names, OrgStructures). Deterministic, composable. Enums provide public API. + +## The Recipe Contract + +Recipes follow strict rules (like a cooking recipe that you follow completely): + +1. A Recipe SHALL have exactly one public method named `Seed()` +2. A Recipe MUST produce one cohesive result (like baking one complete cake) +3. A Recipe MAY have overloaded `Seed()` methods with different parameters +4. A Recipe SHALL use private helper methods for internal steps +5. A Recipe SHALL use BulkCopy for performance when creating multiple entities +6. A Recipe SHALL compose Factories for individual entity creation +7. A Recipe SHALL NOT expose implementation details as public methods + +**Current violations** (to be refactored): + +- `CiphersRecipe` - Uses `AddLoginCiphersToOrganization()` instead of `Seed()` +- `CollectionsRecipe` - Uses `AddFromStructure()` and `AddToOrganization()` instead of `Seed()` +- `GroupsRecipe` - Uses `AddToOrganization()` instead of `Seed()` +- `OrganizationDomainRecipe` - Uses `AddVerifiedDomainToOrganization()` instead of `Seed()` + +## Pattern Decision Tree + +``` +Need to create test data? +├─ ONE entity with encryption? → Factory +├─ MANY entities as cohesive operation? → Recipe +├─ Complete test scenario with ID mangling? → Scene +├─ READ existing seeded data? → Query +└─ Data transformation SDK ↔ Server? → Model +``` + ## When to Use the Seeder ✅ Use for: @@ -56,17 +102,6 @@ CipherView (plaintext) → encrypt_composite() → Cipher (encrypted) Cipher (encrypted) → decrypt() → CipherView (plaintext) ``` -### EncString Format - -All encrypted fields use the EncString format: - -``` -2.{base64_iv}|{base64_data}|{base64_mac} -│ └──────────┘ └──────────┘ └──────────┘ -│ IV Ciphertext HMAC -└─ Type 2 = AES-256-CBC-HMAC-SHA256 -``` - ### SDK vs Server Format Difference **Critical:** The SDK and server use different JSON structures. @@ -93,8 +128,6 @@ All encrypted fields use the EncString format: } ``` -The `CipherSeeder.TransformToServerCipher()` method performs this flattening. - ### Data Flow in Seeder ``` @@ -117,15 +150,6 @@ The `CipherSeeder.TransformToServerCipher()` method performs this flattening. └─────────────────┘ └──────────────────┘ └─────────────────────┘ ``` -### Key Files - -| File | Purpose | -| ------------------------------ | ------------------------------------------------------ | -| `Models/CipherViewDto.cs` | Plaintext input matching SDK's CipherView | -| `Models/EncryptedCipherDto.cs` | Parses SDK's encrypted Cipher output | -| `Factories/CipherSeeder.cs` | Creates encrypted ciphers, transforms to server format | -| `Recipes/CiphersRecipe.cs` | Bulk cipher creation with collection assignment | - ### Key Hierarchy Bitwarden uses a two-level encryption hierarchy: @@ -137,14 +161,6 @@ For seeding, we use the organization's symmetric key directly (no per-cipher key ## Rust SDK FFI -### Available Functions - -| Function | Input | Output | -| ---------------------------- | --------------------- | --------------------------- | -| `encrypt_cipher` | CipherView JSON + key | Cipher JSON | -| `decrypt_cipher` | Cipher JSON + key | CipherView JSON | -| `generate_organization_keys` | (none) | Org symmetric key + keypair | - ### Error Handling SDK functions return JSON with an `"error"` field on failure: @@ -157,7 +173,7 @@ Always check for `"error"` in the response before parsing. ## Testing -Integration tests in `test/SeederApi.IntegrationTest/RustSdkCipherTests.cs` verify: +Integration tests in `test/SeederApi.IntegrationTest` verify: 1. **Roundtrip encryption** - Encrypt then decrypt preserves plaintext 2. **Server format compatibility** - Output matches CipherLoginData structure diff --git a/util/Seeder/README.md b/util/Seeder/README.md index fe758e3a8a..3c840cd5ef 100644 --- a/util/Seeder/README.md +++ b/util/Seeder/README.md @@ -6,19 +6,21 @@ A class library for generating and inserting properly encrypted test data into B ### Cipher Encryption States -| Term | Description | Stored in DB? | -|------|-------------|---------------| -| **CipherView** | Plaintext/decrypted form. Human-readable data. | Never | -| **Cipher** | Encrypted form. All sensitive fields are EncStrings. | Yes | +| Term | Description | Stored in DB? | +| -------------- | ---------------------------------------------------- | ------------- | +| **CipherView** | Plaintext/decrypted form. Human-readable data. | Never | +| **Cipher** | Encrypted form. All sensitive fields are EncStrings. | Yes | The "View" suffix always denotes plaintext. No suffix means encrypted. ### EncString Format Encrypted strings follow this format: + ``` 2.{iv}|{ciphertext}|{mac} ``` + - **2** = Algorithm type (AES-256-CBC-HMAC-SHA256) - **iv** = Initialization vector (base64) - **ciphertext** = Encrypted data (base64) @@ -27,11 +29,13 @@ Encrypted strings follow this format: ### Data Structure Differences **SDK Structure (nested):** + ```json { "name": "2.x...", "login": { "username": "2.y...", "password": "2.z..." } } ``` **Server Structure (flat, stored in Cipher.Data):** + ```json { "Name": "2.x...", "Username": "2.y...", "Password": "2.z..." } ``` @@ -62,38 +66,120 @@ Organization Ciphers belong to organizations and are assigned to collections via the `CollectionCipher` join table. -## Project Structure +### Project Structure -### Factories +The Seeder is organized around six core patterns, each with a specific responsibility: -Create individual domain entities with realistic encrypted data. +#### Factories -| Factory | Purpose | -|---------|---------| -| `CipherSeeder` | Creates encrypted Cipher entities via Rust SDK | -| `CollectionSeeder` | Creates collections with encrypted names | -| `OrganizationSeeder` | Creates organizations with keys | -| `UserSeeder` | Creates users with encrypted credentials | +**Purpose:** Create individual domain entities with cryptographically correct encrypted data. -### Recipes +**Metaphor:** Skilled craftspeople who create one perfect item per call. -Bulk data operations using BulkCopy for performance. +**When to use:** Need to create ONE entity (user, cipher, collection) with proper encryption. -| Recipe | Purpose | -|--------|---------| -| `CiphersRecipe` | Bulk create ciphers and assign to collections | -| `CollectionsRecipe` | Create collections with user permissions | -| `GroupsRecipe` | Create groups with collection access | -| `OrganizationWithUsersRecipe` | Full org setup with users | +**Key characteristics:** -### Models +- Create ONE entity per method call +- Handle encryption/transformation internally +- Stateless (except for SDK service dependency) +- Return fully-formed entity ready for persistence +- Do NOT interact with database directly -DTOs for SDK interop and data transformation. +**Naming:** `{Entity}Seeder` class with `Create{Type}{Entity}()` methods -| Model | Purpose | -|-------|---------| -| `CipherViewDto` | Plaintext input to SDK encryption | -| `EncryptedCipherDto` | Parses SDK's encrypted output | +--- + +#### Recipes + +**Purpose:** Orchestrate cohesive bulk operations using BulkCopy for performance. + +**Metaphor:** Cooking recipes that produce one complete result through coordinated steps. Like baking a three-layer cake - you don't grab three separate recipes and stack them; you follow one comprehensive recipe that orchestrates all the steps. + +**When to use:** Need to create MANY related entities as one cohesive operation (e.g., organization + users + collections + ciphers). + +**Key characteristics:** + +- Orchestrate multiple entity creations as a cohesive operation +- Use BulkCopy for performance optimization +- Interact with database directly +- Compose Factories for individual entity creation +- **SHALL have a `Seed()` method** that executes the complete recipe +- Use method parameters (with defaults) for variations, not separate methods + +**Naming:** `{DomainConcept}Recipe` class with primary `Seed()` method + +**Note:** Some existing recipes violate the `Seed()` method convention and will be refactored in the future. + +--- + +#### Models + +**Purpose:** DTOs that bridge the gap between SDK encryption format and server storage format. + +**Metaphor:** Translators between two different languages (SDK format vs. Server format). + +**When to use:** Need data transformation during the encryption pipeline (SDK → Server format). + +**Key characteristics:** + +- Pure data structures (DTOs) +- No business logic +- Handle serialization/deserialization +- Bridge SDK ↔ Server format differences + +#### Scenes + +**Purpose:** Create complete, isolated test scenarios for integration tests. + +**Metaphor:** Theater scenes with multiple actors and props arranged to tell a complete story. + +**When to use:** Need a complete test scenario with proper ID mangling for test isolation. + +**Key characteristics:** + +- Implement `IScene` or `IScene` +- Create complete, realistic test scenarios +- Handle ID mangling for test isolation +- Return `SceneResult` with mangle map for test assertions +- Async operations +- CAN modify database state + +**Naming:** `{Scenario}Scene` class with `SeedAsync(Request)` method (defined by interface) + +#### Queries + +**Purpose:** Read-only data retrieval for test assertions and verification. + +**Metaphor:** Information desks that answer questions without changing anything. + +**When to use:** Need to READ existing seeded data for verification or follow-up operations. + +**Key characteristics:** + +- Implement `IQuery` +- Synchronous operations (vs. async Scenes) +- Read-only (no database modifications) +- Return typed data for test assertions +- Often used to verify Scene/Recipe results + +**Naming:** `{DataToRetrieve}Query` class with `Execute(Request)` method (defined by interface) + +#### Data + +**Purpose:** Reusable, realistic test data collections that provide the foundation for cipher generation. + +**Metaphor:** A well-stocked ingredient pantry that all recipes draw from. + +**When to use:** Need realistic, filterable data for cipher content (company names, passwords, usernames). + +**Key characteristics:** + +- Static readonly arrays and classes +- Filterable by region, type, category +- Deterministic (seeded randomness for reproducibility) +- Composable across regions +- Enums provide the public API (CompanyType, PasswordStrength, etc.) ## Rust SDK Integration