mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-26 23:04:57 +00:00
553 lines
13 KiB
Markdown
553 lines
13 KiB
Markdown
# Migration Guide: Integrating Secure Database Layer
|
|
|
|
This guide walks you through migrating your existing codebase to use the secure database abstraction layer.
|
|
|
|
## Quick Start
|
|
|
|
### 1. Import the Secure Layer
|
|
|
|
```typescript
|
|
import { SecureDatabase, SecurityContext } from '@/lib/secure-db-layer'
|
|
```
|
|
|
|
### 2. Create Security Context
|
|
|
|
In your component or function, create a security context with the current user:
|
|
|
|
```typescript
|
|
const ctx: SecurityContext = {
|
|
user: currentUser,
|
|
ipAddress: '192.168.1.1', // Optional
|
|
requestId: 'unique-request-id' // Optional
|
|
}
|
|
```
|
|
|
|
### 3. Replace Database Calls
|
|
|
|
Replace direct Prisma or database calls with SecureDatabase methods.
|
|
|
|
## Common Migration Patterns
|
|
|
|
### User Operations
|
|
|
|
**Before (Direct Prisma):**
|
|
```typescript
|
|
import { prisma } from '@/lib/prisma'
|
|
|
|
const users = await prisma.user.findMany()
|
|
const user = await prisma.user.findUnique({ where: { id: userId } })
|
|
await prisma.user.create({ data: { ... } })
|
|
await prisma.user.update({ where: { id: userId }, data: { ... } })
|
|
await prisma.user.delete({ where: { id: userId } })
|
|
```
|
|
|
|
**After (Secure Layer):**
|
|
```typescript
|
|
import { SecureDatabase, SecurityContext } from '@/lib/secure-db-layer'
|
|
|
|
const ctx: SecurityContext = { user: currentUser }
|
|
|
|
const users = await SecureDatabase.getUsers(ctx)
|
|
const user = await SecureDatabase.getUserById(ctx, userId)
|
|
await SecureDatabase.createUser(ctx, userData)
|
|
await SecureDatabase.updateUser(ctx, userId, updates)
|
|
await SecureDatabase.deleteUser(ctx, userId)
|
|
```
|
|
|
|
### Credential Operations
|
|
|
|
**Before:**
|
|
```typescript
|
|
const credential = await prisma.credential.findUnique({
|
|
where: { username }
|
|
})
|
|
const isValid = credential?.passwordHash === providedHash
|
|
|
|
await prisma.credential.upsert({
|
|
where: { username },
|
|
update: { passwordHash },
|
|
create: { username, passwordHash }
|
|
})
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
const ctx: SecurityContext = { user: systemUser }
|
|
|
|
const isValid = await SecureDatabase.verifyCredentials(ctx, username, password)
|
|
await SecureDatabase.setCredential(ctx, username, passwordHash)
|
|
```
|
|
|
|
### Comment Operations
|
|
|
|
**Before:**
|
|
```typescript
|
|
const comments = await prisma.comment.findMany()
|
|
await prisma.comment.create({
|
|
data: {
|
|
id: `comment_${Date.now()}`,
|
|
userId: currentUser.id,
|
|
content: commentText,
|
|
createdAt: BigInt(Date.now())
|
|
}
|
|
})
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
const ctx: SecurityContext = { user: currentUser }
|
|
|
|
const comments = await SecureDatabase.getComments(ctx)
|
|
await SecureDatabase.createComment(ctx, {
|
|
userId: currentUser.id,
|
|
content: commentText
|
|
})
|
|
```
|
|
|
|
### Workflow Operations
|
|
|
|
**Before:**
|
|
```typescript
|
|
const workflows = await prisma.workflow.findMany()
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
const ctx: SecurityContext = { user: currentUser }
|
|
|
|
const workflows = await SecureDatabase.getWorkflows(ctx)
|
|
```
|
|
|
|
## Component Integration Examples
|
|
|
|
### Level 2 Component (User Area)
|
|
|
|
```typescript
|
|
import { useState, useEffect } from 'react'
|
|
import { SecureDatabase, SecurityContext } from '@/lib/secure-db-layer'
|
|
import type { User, Comment } from '@/lib/level-types'
|
|
|
|
interface Level2Props {
|
|
user: User
|
|
onLogout: () => void
|
|
}
|
|
|
|
export function Level2({ user, onLogout }: Level2Props) {
|
|
const [comments, setComments] = useState<Comment[]>([])
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const ctx: SecurityContext = { user }
|
|
|
|
useEffect(() => {
|
|
loadComments()
|
|
}, [])
|
|
|
|
const loadComments = async () => {
|
|
setLoading(true)
|
|
setError(null)
|
|
try {
|
|
const data = await SecureDatabase.getComments(ctx)
|
|
setComments(data)
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to load comments')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const handleAddComment = async (content: string) => {
|
|
try {
|
|
await SecureDatabase.createComment(ctx, {
|
|
userId: user.id,
|
|
content
|
|
})
|
|
await loadComments() // Reload
|
|
toast.success('Comment added!')
|
|
} catch (err) {
|
|
toast.error(err instanceof Error ? err.message : 'Failed to add comment')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{/* Your UI here */}
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Level 3 Component (Admin Panel)
|
|
|
|
```typescript
|
|
import { useState, useEffect } from 'react'
|
|
import { SecureDatabase, SecurityContext } from '@/lib/secure-db-layer'
|
|
import type { User } from '@/lib/level-types'
|
|
|
|
interface Level3Props {
|
|
user: User
|
|
onLogout: () => void
|
|
}
|
|
|
|
export function Level3({ user, onLogout }: Level3Props) {
|
|
const [users, setUsers] = useState<User[]>([])
|
|
const ctx: SecurityContext = { user }
|
|
|
|
useEffect(() => {
|
|
loadUsers()
|
|
}, [])
|
|
|
|
const loadUsers = async () => {
|
|
try {
|
|
const data = await SecureDatabase.getUsers(ctx)
|
|
setUsers(data)
|
|
} catch (err) {
|
|
toast.error('Failed to load users')
|
|
}
|
|
}
|
|
|
|
const handleUpdateUser = async (userId: string, updates: Partial<User>) => {
|
|
try {
|
|
await SecureDatabase.updateUser(ctx, userId, updates)
|
|
await loadUsers() // Reload
|
|
toast.success('User updated!')
|
|
} catch (err) {
|
|
toast.error(err instanceof Error ? err.message : 'Failed to update user')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{/* Admin UI here */}
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Level 4 Component (God Tier)
|
|
|
|
```typescript
|
|
import { useState, useEffect } from 'react'
|
|
import { SecureDatabase, SecurityContext, AuditLog } from '@/lib/secure-db-layer'
|
|
import type { User, Workflow, LuaScript, PageConfig } from '@/lib/level-types'
|
|
|
|
interface Level4Props {
|
|
user: User
|
|
onLogout: () => void
|
|
}
|
|
|
|
export function Level4({ user, onLogout }: Level4Props) {
|
|
const [workflows, setWorkflows] = useState<Workflow[]>([])
|
|
const [luaScripts, setLuaScripts] = useState<LuaScript[]>([])
|
|
const [pageConfigs, setPageConfigs] = useState<PageConfig[]>([])
|
|
const [auditLogs, setAuditLogs] = useState<AuditLog[]>([])
|
|
|
|
const ctx: SecurityContext = { user }
|
|
|
|
useEffect(() => {
|
|
loadAllData()
|
|
}, [])
|
|
|
|
const loadAllData = async () => {
|
|
try {
|
|
const [wf, lua, pages, logs] = await Promise.all([
|
|
SecureDatabase.getWorkflows(ctx),
|
|
SecureDatabase.getLuaScripts(ctx),
|
|
SecureDatabase.getPageConfigs(ctx),
|
|
SecureDatabase.getAuditLogs(ctx, 100)
|
|
])
|
|
|
|
setWorkflows(wf)
|
|
setLuaScripts(lua)
|
|
setPageConfigs(pages)
|
|
setAuditLogs(logs)
|
|
} catch (err) {
|
|
toast.error('Failed to load data')
|
|
}
|
|
}
|
|
|
|
const handleCreateUser = async (userData: any) => {
|
|
try {
|
|
await SecureDatabase.createUser(ctx, userData)
|
|
toast.success('User created!')
|
|
} catch (err) {
|
|
toast.error(err instanceof Error ? err.message : 'Failed to create user')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{/* God tier UI here */}
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Error Handling Best Practices
|
|
|
|
### Always Use Try-Catch
|
|
|
|
```typescript
|
|
try {
|
|
const result = await SecureDatabase.getUsers(ctx)
|
|
// Handle success
|
|
} catch (err) {
|
|
if (err instanceof Error) {
|
|
if (err.message.includes('Rate limit')) {
|
|
toast.error('Too many requests. Please wait a moment.')
|
|
} else if (err.message.includes('Access denied')) {
|
|
toast.error('You do not have permission to perform this action.')
|
|
} else {
|
|
toast.error(err.message)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Handle Loading States
|
|
|
|
```typescript
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const loadData = async () => {
|
|
setLoading(true)
|
|
try {
|
|
const data = await SecureDatabase.getUsers(ctx)
|
|
setUsers(data)
|
|
} catch (err) {
|
|
// Handle error
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Testing with Secure Layer
|
|
|
|
### Mock the Secure Layer
|
|
|
|
```typescript
|
|
import { vi } from 'vitest'
|
|
import * as SecureDB from '@/lib/secure-db-layer'
|
|
|
|
vi.mock('@/lib/secure-db-layer', () => ({
|
|
SecureDatabase: {
|
|
getUsers: vi.fn().mockResolvedValue([
|
|
{ id: '1', username: 'test', email: 'test@example.com', role: 'user' }
|
|
]),
|
|
createUser: vi.fn().mockResolvedValue({ id: '2', username: 'new' }),
|
|
// ... other methods
|
|
}
|
|
}))
|
|
|
|
describe('MyComponent', () => {
|
|
it('should load users', async () => {
|
|
render(<MyComponent user={testUser} />)
|
|
await waitFor(() => {
|
|
expect(screen.getByText('test')).toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|
|
```
|
|
|
|
### Test Access Control
|
|
|
|
```typescript
|
|
it('should deny access for insufficient permissions', async () => {
|
|
const userCtx: SecurityContext = {
|
|
user: { role: 'user', id: '1', username: 'test' }
|
|
}
|
|
|
|
await expect(
|
|
SecureDatabase.createUser(userCtx, userData)
|
|
).rejects.toThrow('Access denied')
|
|
})
|
|
|
|
it('should allow access for god role', async () => {
|
|
const godCtx: SecurityContext = {
|
|
user: { role: 'god', id: '1', username: 'god' }
|
|
}
|
|
|
|
const user = await SecureDatabase.createUser(godCtx, userData)
|
|
expect(user).toBeDefined()
|
|
})
|
|
```
|
|
|
|
## Performance Tips
|
|
|
|
### Batch Operations
|
|
|
|
When loading multiple resources, use `Promise.all`:
|
|
|
|
```typescript
|
|
const [users, workflows, scripts] = await Promise.all([
|
|
SecureDatabase.getUsers(ctx),
|
|
SecureDatabase.getWorkflows(ctx),
|
|
SecureDatabase.getLuaScripts(ctx)
|
|
])
|
|
```
|
|
|
|
### Cache Results
|
|
|
|
For data that doesn't change often:
|
|
|
|
```typescript
|
|
const [users, setUsers] = useState<User[]>([])
|
|
const usersLoadedRef = useRef(false)
|
|
|
|
useEffect(() => {
|
|
if (!usersLoadedRef.current) {
|
|
loadUsers()
|
|
usersLoadedRef.current = true
|
|
}
|
|
}, [])
|
|
```
|
|
|
|
### Debounce Frequent Operations
|
|
|
|
For search or filter operations:
|
|
|
|
```typescript
|
|
import { debounce } from 'lodash'
|
|
|
|
const debouncedSearch = useCallback(
|
|
debounce(async (query: string) => {
|
|
const results = await SecureDatabase.getUsers(ctx)
|
|
// Filter locally or add search method to SecureDatabase
|
|
}, 300),
|
|
[]
|
|
)
|
|
```
|
|
|
|
## Monitoring Integration
|
|
|
|
### Track Operations
|
|
|
|
```typescript
|
|
const trackOperation = async <T,>(
|
|
operationName: string,
|
|
operation: () => Promise<T>
|
|
): Promise<T> => {
|
|
const startTime = Date.now()
|
|
try {
|
|
const result = await operation()
|
|
const duration = Date.now() - startTime
|
|
console.log(`${operationName} completed in ${duration}ms`)
|
|
return result
|
|
} catch (err) {
|
|
const duration = Date.now() - startTime
|
|
console.error(`${operationName} failed after ${duration}ms`, err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
const users = await trackOperation('getUsers', () =>
|
|
SecureDatabase.getUsers(ctx)
|
|
)
|
|
```
|
|
|
|
### Review Audit Logs
|
|
|
|
Add a monitoring dashboard in Level 5:
|
|
|
|
```typescript
|
|
const [auditLogs, setAuditLogs] = useState<AuditLog[]>([])
|
|
|
|
const loadAuditLogs = async () => {
|
|
const logs = await SecureDatabase.getAuditLogs(ctx, 100)
|
|
setAuditLogs(logs)
|
|
|
|
// Check for suspicious activity
|
|
const failedAttempts = logs.filter(log =>
|
|
!log.success && log.operation === 'READ' && log.resource === 'credential'
|
|
)
|
|
|
|
if (failedAttempts.length > 10) {
|
|
toast.warning('High number of failed login attempts detected')
|
|
}
|
|
}
|
|
```
|
|
|
|
## Common Pitfalls
|
|
|
|
### ❌ Don't: Bypass the Secure Layer
|
|
|
|
```typescript
|
|
// DON'T DO THIS
|
|
import { prisma } from '@/lib/prisma'
|
|
const users = await prisma.user.findMany()
|
|
```
|
|
|
|
### ✅ Do: Always Use SecureDatabase
|
|
|
|
```typescript
|
|
// DO THIS
|
|
import { SecureDatabase } from '@/lib/secure-db-layer'
|
|
const users = await SecureDatabase.getUsers(ctx)
|
|
```
|
|
|
|
### ❌ Don't: Forget Security Context
|
|
|
|
```typescript
|
|
// DON'T DO THIS - Missing context
|
|
await SecureDatabase.getUsers()
|
|
```
|
|
|
|
### ✅ Do: Always Provide Context
|
|
|
|
```typescript
|
|
// DO THIS
|
|
const ctx: SecurityContext = { user: currentUser }
|
|
await SecureDatabase.getUsers(ctx)
|
|
```
|
|
|
|
### ❌ Don't: Ignore Errors
|
|
|
|
```typescript
|
|
// DON'T DO THIS
|
|
const users = await SecureDatabase.getUsers(ctx)
|
|
```
|
|
|
|
### ✅ Do: Handle Errors Properly
|
|
|
|
```typescript
|
|
// DO THIS
|
|
try {
|
|
const users = await SecureDatabase.getUsers(ctx)
|
|
// Use users
|
|
} catch (err) {
|
|
console.error('Failed to load users:', err)
|
|
toast.error('Failed to load users')
|
|
}
|
|
```
|
|
|
|
## Checklist for Migration
|
|
|
|
- [ ] Import SecureDatabase in all components that access database
|
|
- [ ] Create SecurityContext with current user
|
|
- [ ] Replace all direct Prisma calls with SecureDatabase methods
|
|
- [ ] Add try-catch error handling around all database operations
|
|
- [ ] Test each role's permissions (user, admin, god, supergod)
|
|
- [ ] Verify rate limiting works (100 requests/minute)
|
|
- [ ] Check audit logs are being created
|
|
- [ ] Update tests to use mocked SecureDatabase
|
|
- [ ] Add loading states for async operations
|
|
- [ ] Document any custom access rules needed
|
|
- [ ] Review and test all edge cases
|
|
- [ ] Monitor performance impact
|
|
- [ ] Set up audit log review process
|
|
|
|
## Next Steps
|
|
|
|
1. **Complete the migration** - Work through each component systematically
|
|
2. **Add tests** - Ensure security rules are enforced
|
|
3. **Monitor audit logs** - Watch for unusual patterns
|
|
4. **Extend as needed** - Add new resources and methods
|
|
5. **Review regularly** - Security is an ongoing process
|
|
|
|
For questions or issues, refer to:
|
|
- `docs/SECURE_DATABASE_LAYER.md` - Full documentation
|
|
- `src/lib/secure-db-layer.ts` - Implementation
|
|
- `src/lib/level-types.ts` - Type definitions
|