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:
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user