UNPKG

@flavoai/fastfold

Version:

Zero-boilerplate backend for React apps with auto-generated CRUD and declarative security

200 lines 7.3 kB
// ============================================================================ // SECURITY API - Clean One-Liners ✨ // ============================================================================ export class Security { /** * 🔓 PUBLIC ACCESS - Anyone can access this table * Perfect for: blogs, public content, marketing pages * * Example: Security.public() * Can also be used as Express middleware: app.get('/api/endpoint', Security.public(), handler) */ static public() { const rule = { type: 'public' }; // Add middleware function const middleware = (req, res, next) => { // Public access - always allow next(); }; return Object.assign(middleware, rule); } /** * 🔐 ADMIN ONLY - Only admin users can access this table * Perfect for: admin logs, system settings, sensitive data * * Example: Security.admin() * Can also be used as Express middleware: app.post('/api/admin', Security.admin(), handler) */ static admin() { const rule = { type: 'admin' }; // Add middleware function const middleware = (req, res, next) => { const user = req.user; if (!user || user.role !== 'admin') { res.status(403).json({ success: false, error: 'Admin access required' }); return; } next(); }; return Object.assign(middleware, rule); } /** * 👤 OWNER-BASED - Users can only access their own records * Perfect for: user profiles, user posts, private data * * @param ownerField The field that contains the user ID (default: 'userId') * * Example: Security.owner('userId') or Security.owner() * Can also be used as Express middleware: app.get('/api/posts', Security.owner('authorId'), handler) */ static owner(ownerField = 'userId') { const rule = { type: 'owner', ownerField }; // Add middleware function const middleware = (req, res, next) => { const user = req.user; if (!user) { res.status(401).json({ success: false, error: 'Authentication required' }); return; } // For owner-based security, the actual check happens in the SecurityEnforcer // This middleware just ensures the user is authenticated next(); }; return Object.assign(middleware, rule); } /** * 🔑 AUTHENTICATED - Any logged-in user can access * Perfect for: user dashboards, protected content * * Example: Security.authenticated() * Can also be used as Express middleware: app.get('/api/profile', Security.authenticated(), handler) */ static authenticated() { const rule = { type: 'authenticated' // Adding this type }; // Add middleware function const middleware = (req, res, next) => { const user = req.user; if (!user) { res.status(401).json({ success: false, error: 'Authentication required' }); return; } next(); }; return Object.assign(middleware, rule); } /** * ⚙️ CUSTOM RULE - Define your own security logic * Perfect for: complex business rules, team-based access * * @param rule Custom function that returns true/false for access * * Example: Security.custom((ctx) => ctx.user?.teamId === ctx.data?.teamId) * Can also be used as Express middleware: app.post('/api/data', Security.custom(myRule), handler) */ static custom(rule) { const securityRule = { type: 'custom', customRule: rule }; // Add middleware function const middleware = (req, res, next) => { const user = req.user; // Custom rules typically need authentication, but the actual logic // is handled by the SecurityEnforcer if (!user) { res.status(401).json({ success: false, error: 'Authentication required' }); return; } next(); }; return Object.assign(middleware, securityRule); } // ============================================================================ // CONVENIENCE METHODS - Even more specific use cases // ============================================================================ /** * 👥 TEAM-BASED - Users can only access records from their team * * @param teamField The field that contains the team ID (default: 'teamId') */ static team(teamField = 'teamId') { return Security.custom((ctx) => { return ctx.user?.teamId === ctx.data?.[teamField] || ctx.user?.teamId === ctx.existingData?.[teamField]; }); } /** * 📝 READ-ONLY PUBLIC - Anyone can read, only admins can write * Perfect for: announcements, company info */ static readOnlyPublic() { return Security.custom((ctx) => { if (ctx.operation === 'read') return true; return ctx.user?.role === 'admin'; }); } } // ============================================================================ // SECURITY ENFORCER - Internal implementation // ============================================================================ export class SecurityEnforcer { static async checkAccess(rule, context) { try { switch (rule.type) { case 'public': return true; case 'admin': return context.user?.role === 'admin'; case 'owner': if (!context.user) return false; const ownerField = rule.ownerField || 'userId'; // For create operations, check the data being created if (context.operation === 'create') { return context.data?.[ownerField] === context.user.id; } // For read/update/delete, check existing data return context.existingData?.[ownerField] === context.user.id; case 'custom': if (!rule.customRule) return false; const result = await rule.customRule(context); return Boolean(result); default: return false; } } catch (error) { console.error('Security check failed:', error); return false; // Fail secure } } static createUnauthorizedError(operation, tableName) { return new Error(`Unauthorized ${operation} operation on table '${tableName}'`); } } // Export for convenience export default Security; //# sourceMappingURL=index.js.map