feat: roles/permissions system and Nostr profile display on admin users
Introduce granular role-based permissions with SuperAdmin env override, admin roles UI, and permission-gated API routes. Fix admin user Nostr metadata by batching relay profile fetches, normalizing npub pubkeys to hex, and adding reusable NostrAvatar/useNostrProfile components. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { authService, type ResolvedAccess } from '../services/auth';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'change-me-in-production';
|
||||
|
||||
@@ -12,6 +13,7 @@ declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: AuthPayload;
|
||||
access?: ResolvedAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,16 +35,26 @@ export function requireAuth(req: Request, res: Response, next: NextFunction): vo
|
||||
}
|
||||
}
|
||||
|
||||
export function requireRole(roles: string[]) {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
// Resolves the requester's effective access (env SuperAdmin list plus database)
|
||||
// and gates the request on a single permission key. SuperAdmin bypasses every
|
||||
// check. The resolved access is attached to req.access for downstream handlers.
|
||||
export function requires(permission: string) {
|
||||
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
if (!req.user) {
|
||||
res.status(401).json({ error: 'Not authenticated' });
|
||||
return;
|
||||
}
|
||||
if (!roles.includes(req.user.role)) {
|
||||
try {
|
||||
const access = await authService.resolveAccess(req.user.pubkey);
|
||||
req.access = access;
|
||||
if (access.isSuperAdmin || access.permissions.has(permission)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
res.status(403).json({ error: 'Insufficient permissions' });
|
||||
return;
|
||||
} catch (err) {
|
||||
console.error('Permission check error:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user