# 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([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(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 (
{/* Your UI here */}
) } ``` ### 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([]) 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) => { 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 (
{/* Admin UI here */}
) } ``` ### 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([]) const [luaScripts, setLuaScripts] = useState([]) const [pageConfigs, setPageConfigs] = useState([]) const [auditLogs, setAuditLogs] = useState([]) 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 (
{/* God tier UI here */}
) } ``` ## 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() 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([]) 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 ( operationName: string, operation: () => Promise ): Promise => { 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([]) 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