Skip to content

Middleware and Services

The Stratpoint Timesheet Application employs a comprehensive middleware stack and service layer architecture to handle cross-cutting concerns, business logic, and system integrations. This section details the implementation and usage of these critical components.

Middleware Architecture

Middleware Stack Overview

graph TB
    A[HTTP Request] --> B[Global Middleware]
    B --> C[Route Middleware]
    C --> D[Controller Middleware]
    D --> E[Controller Action]
    E --> F[Response Middleware]
    F --> G[HTTP Response]

    H[Middleware Types] --> I[Authentication]
    H --> J[Authorization]
    H --> K[Rate Limiting]
    H --> L[CORS]
    H --> M[Logging]
    H --> N[Validation]

Actual Middleware Components

Global Middleware (Applied to All Requests): - TrustProxies: Handle load balancer proxies - TrimStrings: Trim whitespace from inputs - EncryptCookies: Cookie encryption - VerifyCsrfToken: CSRF protection - PreventRequestsDuringMaintenance: Maintenance mode handling

Custom Route Middleware: - my.jwt.auth (MyGetUserFromToken): JWT authentication with SSO support - timesheet.logAccess (LogAccess): Request logging - timesheet.authAppBulongOnly (AuthAppBulongOnly): App-to-app authentication - timesheet.allowedAppSource (AllowedAppSource): App source validation - timesheet.validateAll (ValidateAllUserInputs): Input validation - timesheet.timelogs.handlelastyear (DisableTimelogLastYear): Business rule enforcement

Authentication Middleware

Actual JWT Authentication Middleware

MyGetUserFromToken Class:

<?php

namespace App\Http\Middleware;

use Tymon\JWTAuth\Http\Middleware\Authenticate;
use App\Http\Services\BrewerySsoService;
use App\Http\Services\AuthService;

class MyGetUserFromToken extends Authenticate
{
    public function handle($request, \Closure $next)
    {
        // Check auth method header
        if($request->headers->has('Auth-Method')){
            $frontendAuthMethod = $request->header('Auth-Method');
            if(config('timesheet.authMethod') != $frontendAuthMethod){
                return response()->json([
                    'header' => [
                        'status' => 401,
                        'title' => 'Authenticate',
                        'description' => 'Authentication method changed',
                    ],
                    'body' => [
                        'authMethod' => config('timesheet.authMethod'),
                    ],
                ]);
            } elseif($frontendAuthMethod == 'brewerySso') {
                $checkResult = BrewerySsoService::authenticate($request);
            } else {
                $checkResult = $this->authUsingEmailPass($request);
            }
        } else {
            $checkResult = $this->authUsingEmailPass($request);
        }

        if($checkResult['header']['status'] != 200){
            return response()->json($checkResult, $checkResult['header']['status']);
        } else {
            $request->merge(['authUser' => $checkResult['body']['user']]);
            return $next($request);
        }
    }
}

App-to-App Authentication Middleware

AuthAppBulongOnly Class:

<?php

namespace App\Http\Middleware;

class AuthAppBulongOnly
{
    public function handle($request, \Closure $next, $appName = null)
    {
        $sourceApp = $request->galingsa;        // App source header
        $sourceAppToken = $request->bulong;     // Encrypted token header

        // Decrypt and validate the app source token
        $decryptedText = $this->decryptParam($sourceApp, $sourceAppToken);

        if (!$this->validateSourceAndTime($decryptedText)) {
            return response()->json(['error' => 'Invalid app source'], 401);
        }

        return $next($request);
    }

    private function validateSourceAndTime($decryptedText)
    {
        // Validate that the request comes from authorized app
        // and is within valid time window
        return true; // Implementation details
    }
}

Authorization Middleware

Actual Permission System

Permission checking is done in controllers using helper functions:

// From helpers.php
function hasPermission($user, $permId)
{
    // Permission IDs are stored as comma-separated string
    return $user['isActive'] && strpos($user['permission_ids'], ',' . $permId . ',') !== false;
}

// Usage in controllers:
if (!hasPermission($this->authUser, 57)) {
    return respondAccessNotAllowed($this->title);
}

Controller-level authorization pattern:

class TimelogController extends Controller
{
    public function index($userId, $startDate, $endDate)
    {
        // Check permissions in controller
        if(!hasPermission($this->authUser, 57) ||
           ($userId != $this->authUser['id'] && !hasPermission($this->authUser, 72))) {
            return respondAccessNotAllowed($this->title);
        }
        // Continue with logic...
    }
}

Request Logging Middleware

LogAccess Class:

<?php

namespace App\Http\Middleware;

class LogAccess
{
    public function handle($request, \Closure $next, $appSource)
    {
        // Log API access for monitoring
        $logData = [
            'app_source' => $appSource,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'endpoint' => $request->path(),
            'method' => $request->method(),
            'timestamp' => now()
        ];

        \Log::info('API Access', $logData);

        return $next($request);
    }
}

Rate Limiting Middleware

Built-in Rate Limiting

Laravel's throttle middleware is used:

// From routes/api.php
Route::group(['prefix' => 'v2', 'middleware' => ['timesheet.logAccess:timesheetweb', 'throttle:100,1']], function () {
    // API routes with 100 requests per minute limit
});

Rate limits by endpoint: - General API: 100 requests per minute - Integration APIs: 50 requests per minute (F1, SINOP, CSAT, Wookie)

Service Layer Architecture

Actual Service Classes

Real service classes in the codebase:

// Actual services in app/Http/Services/
- AuthService: JWT authentication and app source validation
- BrewerySsoService: SSO infrastructure (not active)
- EncryptService: Encryption/decryption utilities
- FileExportService: File export operations
- FileUploadService: File upload handling
- PaymentMilestoneService: Payment milestone management
- ProjectManagerService: Project manager utilities
- ProjectResourceService: Project resource management
- ProjectRevenuePMPercentageService: Revenue percentage calculations
- ProjectService: Project-related business logic
- UserService: User management operations
- UtilizationService: Resource utilization calculations

Note: There is no base service class - each service is implemented independently with specific business logic.

Business Logic in Controllers

The application implements business logic primarily in controllers rather than dedicated service classes:

// Example from TimelogController
class TimelogController extends Controller
{
    public function store()
    {
        // Business logic implemented directly in controller
        // Validation, calculations, and database operations
        // Email notifications handled via controller methods
    }

    public function changeStatus()
    {
        // Timelog approval/rejection logic in controller
        // Permission checks using helper functions
        // Database updates and notifications
    }
}

Service classes focus on specific utilities: - File operations (FileUploadService, FileExportService) - Calculations (UtilizationService, ProjectRevenuePMPercentageService) - External integrations (BrewerySsoService)

Email Notifications

Email handling is done through Laravel Mail classes and scheduled commands:

// Mail classes in app/Mail/
- RejectedTimelog: Timelog rejection notifications
- ChargedPassedEndDate: End date notifications
- OffsetSummaryForTheMonth: Monthly offset summaries

// Usage in controllers:
Mail::to($user->email)->send(new RejectedTimelog($timelog));

Bulk email notifications are handled via scheduled Artisan commands rather than a notification service.

Caching Implementation

Caching is used directly in models and controllers:

// Example from User model static methods
public static function getFilters($options = [])
{
    $cacheName = 'users.filters';
    return Cache::remember($cacheName, 3600, function () {
        return self::select(['id', 'name', 'email'])
                   ->where('is_active', 1)
                   ->get();
    });
}

No dedicated cache service class exists - caching is implemented at the point of use with Laravel's Cache facade.

This comprehensive middleware and service architecture provides robust security, business logic handling, and system integration capabilities for the Stratpoint Timesheet Application.