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.