mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-05-03 10:14:52 +00:00
df5398a7ee
Complete implementation of enterprise-grade authentication middleware for email service: Features: - JWT token creation/validation with configurable expiration - Bearer token extraction and validation - Multi-tenant isolation enforced at middleware level - Role-based access control (RBAC) with user/admin roles - Row-level security (RLS) for resource access - Automatic request logging with user context and audit trail - CORS configuration for email client frontend - Rate limiting (50 req/min per user with Redis backend) - Comprehensive error handling with proper HTTP status codes Implementation: - Enhanced src/middleware/auth.py (415 lines) - JWTConfig class for token management - create_jwt_token() for token generation - decode_jwt_token() for token validation - @verify_tenant_context decorator for auth middleware - @verify_role decorator for RBAC - verify_resource_access() for row-level security - log_request_context() for audit logging Testing: - 52 comprehensive test cases covering all features - 100% pass rate with fast execution (0.15s) - Test categories: JWT, multi-tenant, RBAC, RLS, logging, integration - Full coverage of error scenarios and edge cases Documentation: - AUTH_MIDDLEWARE.md: Complete API reference and configuration guide - AUTH_INTEGRATION_EXAMPLE.py: Real-world usage examples for 5+ scenarios - PHASE_7_SUMMARY.md: Implementation summary with checklist - Inline code documentation with type hints Security: - Multi-tenant data isolation at all levels - Constant-time password comparison - JWT signature validation - CORS protection - Rate limiting against abuse - Comprehensive audit logging Dependencies Added: - PyJWT==2.8.1 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
9.3 KiB
9.3 KiB
Email Parser Plugin - Quick Start Guide
Installation
The plugin is already integrated into the email plugins workspace. It's available at:
@metabuilder/workflow-plugin-email-parser
Basic Usage
1. Simple Email Parsing
import { emailParserExecutor, EmailParserConfig } from '@metabuilder/workflow-plugin-email-parser';
const rawEmail = `From: sender@example.com
To: recipient@example.com
Subject: Hello World
Date: Mon, 23 Jan 2026 14:30:45 +0000
This is the email body.`;
const config: EmailParserConfig = {
rawMessage: rawEmail,
tenantId: 'my-tenant'
};
const node = {
id: 'parse-1',
type: 'email-parser',
parameters: config
};
const result = await emailParserExecutor.execute(node, {}, {});
if (result.status === 'success') {
const message = result.output.message;
console.log(`From: ${message.from}`);
console.log(`Subject: ${message.subject}`);
console.log(`Body: ${message.textBody}`);
}
2. Workflow Configuration
Add to your workflow JSON:
{
"id": "email-parser-node",
"type": "email-parser",
"parameters": {
"rawMessage": "{{ $json.rawEmailData }}",
"tenantId": "{{ $context.tenantId }}",
"sanitizeHtml": true
}
}
3. Access Parsed Data
In subsequent workflow nodes, access the parsed email:
{
"id": "store-node",
"type": "dbal-write",
"parameters": {
"entity": "EmailMessage",
"data": {
"from": "{{ $json.parsedEmail.from }}",
"to": "{{ $json.parsedEmail.to }}",
"subject": "{{ $json.parsedEmail.subject }}",
"textBody": "{{ $json.parsedEmail.textBody }}",
"htmlBody": "{{ $json.parsedEmail.htmlBody }}",
"headers": "{{ $json.parsedEmail.headers }}"
}
},
"connections": ["email-parser-node"]
}
Configuration Examples
Parse with HTML Sanitization (Recommended)
const config: EmailParserConfig = {
rawMessage: email,
tenantId: 'tenant-123',
sanitizeHtml: true, // ✓ Remove dangerous HTML
extractAttachmentContent: false // ✓ Metadata only
};
Parse with Attachment Content
For small attachments (< 10MB), extract content:
const config: EmailParserConfig = {
rawMessage: email,
tenantId: 'tenant-123',
extractAttachmentContent: true, // ✓ Include base64 content
maxAttachmentSize: 10 * 1024 * 1024 // 10MB limit
};
Parse with Size Limits
const config: EmailParserConfig = {
rawMessage: email,
tenantId: 'tenant-123',
maxBodyLength: 500 * 1024, // 500KB body limit
maxAttachmentSize: 50 * 1024 * 1024 // 50MB per file
};
Output Reference
Success Response
{
status: 'success',
output: {
message: {
messageId: '<123@example.com>',
from: 'alice@example.com',
to: ['bob@example.com'],
cc: ['manager@company.com'],
subject: 'Meeting Tomorrow',
textBody: 'Let\'s meet at 2pm',
htmlBody: '<p>Let\'s meet at 2pm</p>',
headers: { /* all headers */ },
receivedAt: '2026-01-24T10:30:00.000Z',
attachmentCount: 1,
attachments: [{
filename: 'agenda.pdf',
mimeType: 'application/pdf',
size: 245234,
isInline: false,
contentEncoding: 'base64'
}],
size: 1024
},
errors: [],
warnings: [],
metrics: {
parseDurationMs: 5,
headerCount: 10,
partCount: 2,
attachmentCount: 1,
attachmentSizeBytes: 245234,
sanitizationWarnings: 0
}
},
duration: 8
}
Partial Parse (With Non-Critical Errors)
{
status: 'partial',
output: {
message: { /* email data */ },
errors: [{
code: 'INVALID_MIME',
message: 'Unexpected boundary format',
recoverable: true
}],
warnings: ['Sanitized 2 dangerous HTML elements']
}
}
Error Response
{
status: 'error',
error: 'Email does not contain "From" header',
errorCode: 'MISSING_FROM',
output: {
errors: [{
code: 'MISSING_FROM',
message: 'Email does not contain "From" header',
recoverable: false
}]
}
}
Common Use Cases
Case 1: Sync from IMAP and Parse
{
"nodes": [
{
"id": "imap-sync",
"type": "imap-sync",
"parameters": { "imapId": "{{ $context.imapId }}", "folderId": "{{ $json.folderId }}" }
},
{
"id": "parse-email",
"type": "email-parser",
"parameters": {
"rawMessage": "{{ $json.messageBody }}",
"tenantId": "{{ $context.tenantId }}"
},
"connections": ["imap-sync"]
}
]
}
Case 2: Parse and Store in Database
{
"nodes": [
{
"id": "parse-email",
"type": "email-parser",
"parameters": { "rawMessage": "{{ $json.email }}", "tenantId": "{{ $context.tenantId }}" }
},
{
"id": "store-message",
"type": "dbal-write",
"parameters": {
"entity": "EmailMessage",
"data": {
"from": "{{ $json.message.from }}",
"to": "{{ JSON.stringify($json.message.to) }}",
"subject": "{{ $json.message.subject }}",
"textBody": "{{ $json.message.textBody }}",
"htmlBody": "{{ $json.message.htmlBody }}"
}
},
"connections": ["parse-email"]
},
{
"id": "store-attachments",
"type": "dbal-write",
"parameters": {
"entity": "EmailAttachment",
"data": "{{ $json.message.attachments }}",
"batchMode": true
},
"connections": ["store-message"]
}
]
}
Case 3: Parse and Search
{
"nodes": [
{
"id": "parse-email",
"type": "email-parser",
"parameters": { "rawMessage": "{{ $json.email }}", "tenantId": "{{ $context.tenantId }}" }
},
{
"id": "search-email",
"type": "email-search",
"parameters": {
"query": "{{ $context.searchQuery }}",
"content": "{{ $json.message.textBody }} {{ $json.message.subject }}"
},
"connections": ["parse-email"]
}
]
}
Validation
Before executing, validate the configuration:
const validation = emailParserExecutor.validate(node);
if (!validation.valid) {
console.error('Validation errors:');
validation.errors.forEach(err => console.error(` - ${err}`));
}
if (validation.warnings.length > 0) {
console.warn('Validation warnings:');
validation.warnings.forEach(warn => console.warn(` - ${warn}`));
}
Error Handling
const result = await emailParserExecutor.execute(node, context, state);
if (result.status === 'error') {
// Critical error - no message parsed
console.error(`Parse failed: ${result.error}`);
console.error(`Error code: ${result.errorCode}`);
// Handle failure
return handleParseError(result.error);
}
if (result.status === 'partial') {
// Non-critical errors but message still available
console.warn(`Parse succeeded with issues`);
if (result.output.errors.length > 0) {
console.warn('Errors:', result.output.errors);
}
if (result.output.warnings.length > 0) {
console.warn('Warnings:', result.output.warnings);
}
// Still process message
processMessage(result.output.message);
}
if (result.status === 'success') {
// Clean parse
processMessage(result.output.message);
}
Performance Tips
-
Don't extract large attachment content:
extractAttachmentContent: false // ← Metadata only -
Set body length limit:
maxBodyLength: 1024 * 1024 // 1MB -
Limit attachment size:
maxAttachmentSize: 25 * 1024 * 1024 // 25MB -
Keep HTML sanitization enabled:
sanitizeHtml: true // ← Default, minimal overhead
Security Best Practices
-
Always enable HTML sanitization for untrusted emails:
sanitizeHtml: true // ✓ Remove XSS vectors -
Never trust sender information:
// Always validate From address in business logic const from = message.from; // Could be spoofed -
Store attachment content separately:
// Don't store base64 content in database // Use S3 or other blob storage instead -
Validate all output before using:
if (!message.from || !message.to || !message.subject) { throw new Error('Invalid email structure'); }
Troubleshooting
Empty body after parsing:
- Check if email has a body section
- Verify Content-Type multipart handling
- Review charset in headers
Attachments not found:
- Verify multipart/mixed structure
- Check Content-Disposition header
- Check maxAttachmentSize limit
HTML missing:
- Check if HTML was actually present in original
- Review sanitization warnings count
- Dangerous content may have been removed
Special characters garbled:
- Check RFC 2047 encoding in headers
- Verify charset parameter in Content-Type
- Review Buffer encoding during decode
Next Steps
- Read README.md for comprehensive documentation
- Read IMPLEMENTATION.md for technical details
- Review test suite in src/index.test.ts for examples
- Integrate into your email client workflow
Support
For issues or questions:
- Check the README.md documentation
- Review IMPLEMENTATION.md for technical details
- Check test suite for usage examples
- Review CLAUDE.md for development guidelines