Case Study Featured Project

SentinelDesk

A Secure-by-Design SOAR (Security Orchestration, Automation, and Response) platform demonstrating enterprise security patterns through real implementation.

Overview

SentinelDesk is a Security Orchestration, Automation, and Response (SOAR) platform designed to demonstrate modern DevSecOps principles. Unlike typical CRUD apps, it features a built-in Threat Detection Engine that monitors user activity in real-time to identify anomalies like impossible travel, brute force attempts, and privilege escalation.

The project includes a "Simulation Mode" that generates realistic attack traffic, allowing users to watch the detection logic trigger alerts and automate incident response workflows.

Demo Screenshots

SentinelDesk Dashboard

Main dashboard with security overview

Sign In Page

Microsoft Entra ID SSO sign-in

Microsoft Authentication

Enterprise authentication with Microsoft

Tickets View

Incident ticket management

Ticket Details

Detailed ticket view with RBAC controls

Security Alerts

Real-time threat detection alerts

Attack Simulation

Simulated attack for detection testing

1 / 7
Click image to enlarge

*Click images to view the specific security controls and detection dashboards.*

⚛️
React 18
TypeScript + Vite
FastAPI
Python 3.11
🔐
Entra ID
OIDC/OAuth
🕵️
SQLAlchemy
SQLite / Postgres

1. Identity & Access Management (IAM)

Authentication is handled through Microsoft Entra ID (formerly Azure AD) using the OIDC protocol. This provides enterprise-grade SSO without managing passwords directly.

Key Implementation Details

  • OIDC Flow: Modern authentication with ID tokens and access tokens
  • JWT Validation: Backend verifies token signatures, issuers, and audiences
  • No Blind Trust: Every request is validated—no session-based assumptions
# FastAPI backend - JWT token validation
async def verify_bearer_token(request: Request) -> Dict[str, Any]:
    auth = request.headers.get("authorization")
    if not auth or not auth.lower().startswith("bearer "):
        raise HTTPException(status_code=401, detail="Missing bearer token")

    token = auth.split(" ", 1)[1].strip()
    jwks = await _get_jwks()

    try:
        # Microsoft Entra ID token validation
        # Verify signature, issuer, and audience against JWKS
        claims = jwt.decode(
            token,
            jwks,
            algorithms=["RS256"],
            audience=settings.api_audience,
        )
        
        # Manual issuer check against valid Entra ID endpoints
        token_issuer = claims.get("iss", "")
        if token_issuer not in valid_issuers:
            raise HTTPException(status_code=401, detail="Invalid issuer")
        
        return claims
    except JWTError as e:
        raise HTTPException(status_code=401, detail="Invalid token")

2. Role-Based Access Control (RBAC)

The platform implements least-privilege access with three distinct roles:

RBAC Matrix

Action Viewer Analyst Admin
View own incidents
View all incidents
Update incidents
Delete incidents
View security alerts
Manage users
# Granular RBAC Implementation
ROLE_PERMS: Dict[str, Set[str]] = {
    "viewer": {"tickets:read", "tickets:create", "tickets:comment"},
    "analyst": {"tickets:read", "tickets:create", "tickets:comment", "tickets:update"},
    "admin": {"tickets:read", "tickets:create", "tickets:update", "tickets:delete", "admin:export_audit"},
}

def require_perm(claims: dict, perm: str) -> None:
    """Check if user has a role that includes the required permission."""
    roles = roles_from_claims(claims) # Maps Entra Group IDs to roles
    allowed = set()
    for r in roles:
        allowed |= ROLE_PERMS.get(r, set())

    if perm not in allowed:
        # This triggers an "authz:denied" audit event in the caller
        raise HTTPException(status_code=403, detail=f"Missing permission: {perm}")

3. Threat Detection Engine

The platform includes real-time detection for common attack patterns:

Impossible Travel

Detects logins from geographically distant IPs within short timeframes. If you can't physically travel that fast, it's likely compromised credentials.

Privilege Escalation Attempts

Flags repeated 403 Forbidden responses on sensitive endpoints. Pattern of access denials may indicate an attacker probing for vulnerabilities.

IDOR Protection

Explicit ownership checks on every resource access. Changing an ID in the URL won't give you access to someone else's data.

# Real-time Threat Detection Logic
def run_detections_for_event(db: Session, event: AuditEvent):
    """Run detection rules against every new audit event."""
    
    # Rule 1: Auth failure burst (Brute Force / Credential Stuffing)
    if event.action == "auth:token_invalid" and event.ip:
        recent = count_recent_events(action="auth:token_invalid", ip=event.ip, minutes=5)
        if recent >= 10:
            _create_alert(db, "AUTH_FAIL_BURST", "high", {"ip": event.ip, "count": recent})

    # Rule 2: Impossible Travel (Same user, different IPs < 5 mins)
    if event.action == "auth:login" and event.actor_sub:
        unique_ips = get_recent_distinct_ips(event.actor_sub, minutes=5)
        if len(unique_ips) >= 2:
            _create_alert(db, "IMPOSSIBLE_TRAVEL", "high", {"ips": unique_ips})

    # Rule 3: Privilege Escalation (Repeated Admin Access Denials)
    if event.action == "authz:denied" and "admin:" in event.target:
        denials = count_recent_denials(event.actor_sub, target_match="admin:", minutes=10)
        if denials >= 3:
            _create_alert(db, "PRIVILEGE_ESCALATION_ATTEMPT", "high", {"count": denials})

4. Vulnerability Lab: IDOR Demo

To demonstrate the importance of secure coding, the project features a "Vulnerability Lab" with two parallel endpoints for education:

  • GET /tickets/insecure/{id}: A naive implementation vulnerable to IDOR. Any authenticated user can view any ticket by changing the ID.
  • GET /tickets/{id}: The secure implementation. It explicitly checks if ticket.owner_sub == current_user.sub before returning data.

Attempting to exploit the secure endpoint triggers a "BLOCKED_IDOR_ATTEMPT" alert in the system.

# VULNERABILITY LAB DEMO
# ❌ INSECURE ENDPOINT (Vulnerable to IDOR)
@router.get("/tickets/insecure/{ticket_id}")
async def get_ticket_insecure(ticket_id: int):
    # DANGEROUS: No ownership check! ANY authenticated user can view this.
    return db.query(Ticket).filter(Ticket.id == ticket_id).first()

# ✅ SECURE ENDPOINT
@router.get("/tickets/{ticket_id}")
async def get_ticket_secure(ticket_id: int, claims: dict = Depends(verify_bearer_token)):
    ticket = db.query(Ticket).filter(Ticket.id == ticket_id).first()
    
    # Secure ownership check
    current_user_sub = claims["sub"]
    if ticket.owner_sub != current_user_sub:
        # Trigger high-severity alert for immediate response
        _create_alert(db, "BLOCKED_IDOR_ATTEMPT", "medium", {"target": ticket_id})
        raise HTTPException(status_code=403, detail="Forbidden")
        
    return ticket

5. Attack Simulation & Audit Logging

A key feature of the platform is the Attack Simulator, capable of generating realistic threat patterns to test the detection engine.

The simulator programmatically generates audit events (e.g., login from New York followed by Login from Tokyo) to verify that "Impossible Travel" alerts are triggered correctly. This closes the feedback loop, ensuring detections actually work in practice.

6. DevSecOps Pipeline

Security isn't just about the code—it's about the entire development lifecycle.

SAST Bandit

Static analysis for Python security issues

✓ 0 High/Medium issues
DAST OWASP ZAP

Dynamic scanning for runtime vulnerabilities

✓ Baseline compliance verified

Infrastructure Security

  • Multi-stage Docker builds: Minimal attack surface in production images
  • Rate limiting: 100 requests/minute to prevent abuse
  • Security headers: CSP, HSTS, X-Frame-Options configured
  • Audit logging: Every security-relevant action is logged

Key Takeaways

Security is not a feature—it's a property of how the system is designed and implemented.

SentinelDesk demonstrates that security can be built in from day one without sacrificing developer experience or functionality. The patterns shown here—OIDC authentication, server-side RBAC, threat detection, and DevSecOps—are directly applicable to enterprise environments.