diff --git a/app/Http/Controllers/ApplicationController.php b/app/Http/Controllers/ApplicationController.php index 53df414..8fd64cb 100644 --- a/app/Http/Controllers/ApplicationController.php +++ b/app/Http/Controllers/ApplicationController.php @@ -4,6 +4,8 @@ use App\Classes\Repositories\ApplicationRepositoryInterface; use App\Classes\Transformers\ApplicationTransformer; +use App\Http\Requests\UpsertApplicationRulesRequest; +use App\Models\Application; use Illuminate\Database\Eloquent\Collection; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -494,4 +496,29 @@ public function deactivate($id) return response()->json($response->toArray(), 200); } + + /** + * Bulk replace rules for all applications belonging to a tenant user. + * + * @param UpsertApplicationRulesRequest $request + * @return \Illuminate\Http\JsonResponse + */ + public function upsertRules(UpsertApplicationRulesRequest $request) + { + $validated = $request->validated(); + + $updated = Application::query() + ->where('tenant_user_id', $validated['tenant_user_id']) + ->update([ + 'rules' => $validated['rules'], + 'updated_at' => now(), + ]); + + return response()->json([ + 'status' => 200, + 'message' => 'Application rules updated successfully.', + 'tenant_user_id' => $validated['tenant_user_id'], + 'updated_count' => $updated, + ], 200); + } } diff --git a/app/Http/Middleware/ApiAuthMiddleware.php b/app/Http/Middleware/ApiAuthMiddleware.php index 01ffeb7..72cdc81 100644 --- a/app/Http/Middleware/ApiAuthMiddleware.php +++ b/app/Http/Middleware/ApiAuthMiddleware.php @@ -6,23 +6,17 @@ use App\Models\UsageLog; use Carbon\Carbon; use Closure; +use Illuminate\Support\Facades\Log; class ApiAuthMiddleware extends BasicAuthMiddleware { - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - */ public function handle($request, Closure $next) { $apiKey = $request->header('x-api-key'); $authHeader = $request->header('Authorization'); $isBasicAuth = $authHeader && str_starts_with($authHeader, 'Basic '); - if (!$apiKey && !$isBasicAuth) { + if (!$apiKey && !$isBasicAuth) { return response()->json(['error' => 'Authentication required. Provide API key or Basic auth'], 401); } @@ -31,15 +25,24 @@ public function handle($request, Closure $next) } if ($apiKey) { - $application = Application::where('key', '=', $apiKey)->first(); + $application = Application::query()->where('key', '=', $apiKey)->first(); if (!$application) { + return response()->json(['error' => 'Invalid API key'], 401); } - if (!$application->is_active) { - return response()->json(['error' => 'Application is inactive'], 403); + + if ($application->trashed() || !$application->is_active) { + return response()->json(['error' => 'Application is unavailable'], 403); } + + $canAccess = $this->canAccessRequestedVersion($request->path(), (array) $application->rules); + + if (!$canAccess) { + return response()->json(['error' => 'Application is not allowed to access this API version'], 403); + } + $usageLog = new UsageLog; $usageLog->application_id = $application->id; $usageLog->method = $request->method(); @@ -55,4 +58,17 @@ public function handle($request, Closure $next) return $next($request); } + + private function canAccessRequestedVersion(string $path, array $rules): bool + { + if (strpos($path, 'v1/') === 0) { + return $rules['can_access_legacy_whatnow']; + } + + if (strpos($path, 'v2/') === 0) { + return $rules['can_access_preparedness_v2']; + } + + return true; + } } diff --git a/app/Http/Requests/UpsertApplicationRulesRequest.php b/app/Http/Requests/UpsertApplicationRulesRequest.php new file mode 100644 index 0000000..d74ca66 --- /dev/null +++ b/app/Http/Requests/UpsertApplicationRulesRequest.php @@ -0,0 +1,32 @@ + 'required|string|max:255', + 'rules' => 'required|array', + ]; + } +} + diff --git a/app/Models/Application.php b/app/Models/Application.php index 1339591..2170df9 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -31,6 +31,7 @@ class Application extends Model 'estimated_users_count', 'key', 'is_active', + 'rules', ]; protected $dates = ['deleted_at']; @@ -42,6 +43,7 @@ class Application extends Model */ protected $casts = [ 'is_active' => 'boolean', + 'rules' => 'array', ]; /** diff --git a/database/migrations/2026_03_04_000001_add_rules_to_applications_table.php b/database/migrations/2026_03_04_000001_add_rules_to_applications_table.php new file mode 100644 index 0000000..b4ff476 --- /dev/null +++ b/database/migrations/2026_03_04_000001_add_rules_to_applications_table.php @@ -0,0 +1,33 @@ +json('rules')->nullable()->after('is_active'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('rules'); + }); + } +} + diff --git a/routes/api.php b/routes/api.php index 5874aac..3594efa 100644 --- a/routes/api.php +++ b/routes/api.php @@ -58,6 +58,7 @@ // Route::delete('org/{code}/image', 'OrganisationController@deleteImageById'); // "Applications" endpoints + Route::post('applications/rules', 'ApplicationController@upsertRules'); Route::get('apps', 'ApplicationController@getAllForUser'); Route::post('apps', 'ApplicationController@create'); Route::get('apps/{id}', 'ApplicationController@getById');