Skip to content

Latest commit

Β 

History

History
582 lines (466 loc) Β· 13.9 KB

README.md

File metadata and controls

582 lines (466 loc) Β· 13.9 KB

Restricted Registered Emails System

A comprehensive security solution for preventing registration with privileged, reserved, or system-critical email addresses. Includes backend API, PostgreSQL database, admin management UI, and integration examples for your registration endpoint.

πŸ”’ Security Features

  • Three Matching Types: Exact match (localpart), domain-level blocking, and regex patterns
  • Multi-Domain Support: Apply global rules or domain-specific restrictions
  • Audit Trail: Complete logging of all blocklist changes with timestamps and user attribution
  • Flexible Management: Add/edit/delete entries, bulk import/export (CSV)
  • Pre-Populated Defaults: Includes common reserved addresses (admin, support, noreply, etc.)

πŸ“‹ Table of Contents

πŸš€ Quick Start

Prerequisites

  • Node.js 18+
  • PostgreSQL 12+
  • npm or yarn

Backend Setup

cd backend
npm install
cp .env.example .env
# Edit .env with your database credentials
npm run db:migrate
npm run dev

Frontend Setup

cd frontend
npm install
npm run dev

Visit http://localhost:3001 to access the admin dashboard.

πŸ—οΈ Architecture

Backend Stack

  • Runtime: Node.js with Express.js
  • Language: TypeScript
  • Database: PostgreSQL with audit triggers
  • API: RESTful API with JSON responses

Frontend Stack

  • Framework: React 18 with TypeScript
  • Build Tool: Vite
  • Styling: Tailwind CSS
  • UI Components: Custom with Lucide icons

Database Schema

restricted_emails
β”œβ”€β”€ id (UUID, PK)
β”œβ”€β”€ pattern (VARCHAR, the email pattern)
β”œβ”€β”€ type (VARCHAR: 'exact' | 'domain' | 'regex')
β”œβ”€β”€ applicable_domains (TEXT[], NULL for all domains)
β”œβ”€β”€ reason (TEXT, optional)
β”œβ”€β”€ created_by (VARCHAR)
β”œβ”€β”€ created_at (TIMESTAMP)
β”œβ”€β”€ updated_at (TIMESTAMP)
β”œβ”€β”€ is_active (BOOLEAN)
└── ...

restricted_emails_audit
β”œβ”€β”€ id (UUID, PK)
β”œβ”€β”€ restricted_email_id (UUID, FK)
β”œβ”€β”€ action (VARCHAR: 'CREATE' | 'UPDATE' | 'DELETE')
β”œβ”€β”€ pattern_before / pattern_after
β”œβ”€β”€ type_before / type_after
β”œβ”€β”€ domains_before / domains_after
β”œβ”€β”€ reason_before / reason_after
β”œβ”€β”€ changed_by (VARCHAR)
β”œβ”€β”€ change_timestamp (TIMESTAMP)
└── ...

πŸ“¦ Installation

1. Clone or Extract Files

cd /path/to/Restricted-Registered-Emails

2. Set Up Backend

cd backend
npm install
cp .env.example .env

Edit .env with your PostgreSQL credentials:

DATABASE_URL=postgresql://postgres:password@localhost:5432/restricted_emails_db
DB_HOST=localhost
DB_PORT=5432
DB_NAME=restricted_emails_db
DB_USER=postgres
DB_PASSWORD=yourpassword
PORT=3000
NODE_ENV=development
CORS_ORIGIN=http://localhost:3001

3. Initialize Database

npm run db:migrate

This will create all tables, indexes, triggers, and seed default restricted emails.

4. Start Backend Server

npm run dev

Server will start on http://localhost:3000

5. Set Up Frontend

cd ../frontend
npm install
npm run dev

Access the admin dashboard at http://localhost:3001

βš™οΈ Configuration

Environment Variables (Backend)

Variable Description Default
DATABASE_URL PostgreSQL connection string -
DB_HOST Database host localhost
DB_PORT Database port 5432
DB_NAME Database name restricted_emails_db
DB_USER Database user postgres
DB_PASSWORD Database password postgres
PORT Server port 3000
NODE_ENV Environment development
CORS_ORIGIN CORS allowed origin http://localhost:3001
JWT_SECRET (Optional) JWT secret for auth your-secret-key

Frontend Configuration

Edit frontend/vite.config.ts to change the API proxy target:

server: {
  proxy: {
    '/api': {
      target: 'http://localhost:3000',  // Change to your backend URL
      changeOrigin: true,
    },
  },
},

πŸ“š API Documentation

Base URL

http://localhost:3000/api/blocklist

Endpoints

1. Get All Restricted Emails

GET /api/blocklist?page=1&pageSize=20&search=admin&type=exact&domain=example.com

Query Parameters:

  • page (integer, default: 1) - Page number
  • pageSize (integer, default: 20) - Entries per page
  • search (string) - Search by pattern
  • type (string) - Filter by type (exact, domain, regex)
  • domain (string) - Filter by applicable domain

Response:

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "pattern": "admin",
      "type": "exact",
      "applicableDomains": null,
      "reason": "Reserved for admin accounts",
      "createdBy": "system",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z",
      "isActive": true
    }
  ],
  "total": 150,
  "page": 1,
  "pageSize": 20,
  "totalPages": 8
}

2. Get Single Entry

GET /api/blocklist/{id}

3. Create Restricted Email

POST /api/blocklist
Content-Type: application/json

{
  "pattern": "admin",
  "type": "exact",
  "applicableDomains": null,
  "reason": "Reserved for administrative accounts"
}

Response: 201 Created with created entry

4. Update Entry

PUT /api/blocklist/{id}
Content-Type: application/json

{
  "reason": "Updated reason"
}

5. Delete Entry

DELETE /api/blocklist/{id}

Response: 204 No Content (soft delete - marks as inactive)

6. Validate Email

POST /api/blocklist/validate
Content-Type: application/json

{
  "email": "admin@example.com",
  "domain": "example.com"
}

Response:

{
  "isBlocked": true,
  "blockedPattern": {
    "id": "...",
    "pattern": "admin",
    "type": "exact",
    "reason": "Reserved for admin accounts"
  },
  "reason": "This email address is reserved for system use."
}

7. Bulk Import

POST /api/blocklist/bulk-import
Content-Type: application/json

{
  "csvContent": "pattern,type,applicable_domains,reason\nadmin,exact,,Admin account\nsupport,exact,,Support team",
  "createdBy": "admin"
}

CSV Format:

pattern,type,applicable_domains,reason
admin,exact,,Reserved for admin accounts
support,exact,,Reserved for support team
noreply,exact,,Reserved for system notifications
security,exact,,Reserved for security team
help,exact,,Reserved for help desk

Response:

{
  "success": true,
  "inserted": 5,
  "errors": [],
  "data": [...]
}

8. Export Blocklist

GET /api/blocklist/export

Response: CSV file download

9. Get Audit Log (for entry)

GET /api/blocklist/{id}/audit?page=1&pageSize=20

10. Get Global Audit Log

GET /api/blocklist/audit/all?page=1&pageSize=20&action=CREATE

πŸ–₯️ Admin Dashboard

The React dashboard provides a user-friendly interface for managing the blocklist.

Features

  1. View Blocklist

    • Paginated table with sorting
    • Search by pattern
    • Filter by type (exact, domain, regex)
    • Filter by applicable domain
  2. Add Entry

    • Pattern input
    • Match type selector
    • Domain picker (multi-select or global)
    • Reason/notes field
  3. Edit Entry

    • Inline editing modal
    • Update any field
    • Automatic audit logging
  4. Delete Entry

    • Soft delete with confirmation
    • Preserves history in audit log
  5. Bulk Operations

    • CSV import with preview
    • Drag-and-drop file upload
    • CSV export with templates
    • Error reporting for failed imports
  6. Audit Log View

    • Track all changes
    • View who changed what and when
    • Filter by action type

πŸ”— Integration Guide

How to Integrate with Your Registration Endpoint

See backend/src/routes/register-example.ts for a complete example.

Step 1: Import the Service

import { EmailBlocklistService } from '../services/emailBlocklist';
import { getPool } from '../database/connection';

const blocklistService = new EmailBlocklistService(getPool());

Step 2: Check Email in Registration Handler

app.post('/api/register', async (req, res) => {
  const { email, password, domain } = req.body;

  // Validate email format
  if (!isValidEmailFormat(email)) {
    return res.status(400).json({ error: 'Invalid email format' });
  }

  // βœ“ KEY STEP: Check if email is blocked
  const blockCheck = await blocklistService.isEmailBlocked(email, domain);
  if (blockCheck.isBlocked) {
    return res.status(403).json({
      error: blockCheck.reason || 'This email address cannot be used for registration',
      code: 'EMAIL_RESTRICTED',
    });
  }

  // Check if already registered
  const existing = await findUserByEmail(email);
  if (existing) {
    return res.status(409).json({ error: 'Email already registered' });
  }

  // Create user account
  const user = await createUser({ email, password });
  
  res.status(201).json({ userId: user.id });
});

Step 3: (Optional) Real-Time Validation Endpoint

app.post('/api/validate-email', async (req, res) => {
  const { email, domain } = req.body;

  const isBlocked = await blocklistService.isEmailBlocked(email, domain);
  const isRegistered = await checkUserExists(email);

  res.json({
    isValid: !isBlocked.isBlocked && !isRegistered,
    isBlocked: isBlocked.isBlocked,
    message: isBlocked.reason,
  });
});

Integration Points

  • Email Validation: Before creating user account in registration/signup endpoint
  • API Endpoints: Make requests to POST /api/blocklist/validate
  • Frontend: Show validation errors in signup form
  • Logging: Log blocked attempts for security monitoring

πŸ§ͺ Testing

Run Unit Tests

cd backend
npm test

Run Specific Test File

npm test -- emailBlocklist.test.ts

Test Coverage

npm test -- --coverage

What's Tested

  • βœ… Exact match email validation
  • βœ… Domain-level blocking
  • βœ… Regex pattern matching
  • βœ… Case-insensitive matching
  • βœ… Whitespace trimming
  • βœ… Domain-specific restrictions
  • βœ… Invalid regex handling
  • βœ… Edge cases (multiple @, missing @, etc.)

🚒 Deployment

Production Checklist

  • Update .env with production database credentials
  • Set NODE_ENV=production
  • Enable HTTPS/SSL
  • Configure CORS for production domain
  • Set up database backups
  • Configure logging and monitoring
  • Rate limit registration endpoint
  • Set up audit log retention policy
  • Deploy backend and frontend separately
  • Test integration with your registration endpoint

Docker Deployment (Optional)

Create backend/Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]

Build and run:

docker build -t restricted-emails-api .
docker run -p 3000:3000 -e DATABASE_URL=... restricted-emails-api

Environment-Specific Configs

Development:

NODE_ENV=development
CORS_ORIGIN=http://localhost:3001

Production:

NODE_ENV=production
CORS_ORIGIN=https://yourdomain.com
DATABASE_URL=postgresql://prod_user:secure_password@prod-db-host:5432/restricted_emails_db

πŸ“Š Matching Types Explained

1. Exact Match

Matches the local part of the email exactly (case-insensitive).

Examples:

  • Pattern: admin β†’ Blocks: admin@any.com, ADMIN@example.com
  • Pattern: support β†’ Blocks: support@example.com, support@other.io

2. Domain Match

Matches specific domain(s).

Examples:

  • Pattern: example.com β†’ Blocks: anyone@example.com, user@sub.example.com
  • Pattern: @domain.com β†’ Blocks: any@domain.com

3. Regex Pattern

Uses regular expressions for complex matching rules.

Examples:

  • Pattern: ^noreply-.*@example\.com$ β†’ Blocks: noreply-system@example.com, noreply-alerts@example.com
  • Pattern: admin.*@(example|test)\.com β†’ Blocks: admin.main@example.com, adminuser@test.com, etc.

πŸ” Common Use Cases

1. Block All Noreply Variations Across All Domains

Pattern: noreply | Type: Exact | Domains: None (global)

2. Block Specific Domain

Pattern: suspicious-domain.com | Type: Domain | Domains: None

3. Block Admin Accounts Only for One Domain

Pattern: admin | Type: Exact | Domains: example.com

4. Block Regex Patterns for Multiple Domains

Pattern: ^(noreply|mailer|daemon)-.* | Type: Regex | Domains: domain1.com, domain2.com

πŸ“ Default Blocklist

Out-of-the-box, these addresses are blocked globally:

  • admin, administrator - Administrative accounts
  • support, help - Support teams
  • noreply, no-reply - Notification systems
  • security, abuse - Security/abuse reporting
  • contact, info - Generic addresses
  • sales - Sales team
  • root, postmaster, mailer-daemon - System accounts

Edit or delete these from the admin dashboard as needed.

πŸ†˜ Troubleshooting

Database Connection Error

Error: connect ECONNREFUSED 127.0.0.1:5432

Solution: Ensure PostgreSQL is running. Check DB_HOST, DB_PORT, DB_USER, DB_PASSWORD in .env.

API Returns 403 for All Registrations

Solution: Check the audit log to see which pattern is blocking. The blocklist may be too strict. Adjust domain restrictions if needed.

Regex Pattern Not Matching

Solution: Test your regex in a tool like regex101.com. Remember patterns are case-insensitive by default.

CORS Errors in Frontend

Solution: Update CORS_ORIGIN in .env to match your frontend URL.

πŸ“„ License

MIT

Support

For issues, questions, or feature requests, please refer to the integration documentation or contact your security team.