1
0
mirror of https://github.com/bitwarden/server synced 2026-01-30 00:03:48 +00:00

Claude & ReadMe markdown improvements

This commit is contained in:
Mick Letofsky
2026-01-27 13:06:22 +01:00
parent 5ec01e5163
commit d645a143e3
2 changed files with 160 additions and 58 deletions

View File

@@ -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<TRequest>`. Async, returns `SceneResult` with MangleMap. Named `{Scenario}Scene`.
5. **Queries** - Read-only data retrieval. Implement `IQuery<TRequest, TResult>`. 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

View File

@@ -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<TRequest>` or `IScene<TRequest, TResult>`
- 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<TRequest, TResult>`
- 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