Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test #116

Merged
merged 2 commits into from
Oct 22, 2024
Merged

Test #116

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions app/Http/Controllers/ServiceAccountController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
use Response;

class ServiceAccountController extends Controller
{
public function fetchData(Request $request)
{
$tableName = $request->input('table', '');
$app = $request->input('app');
$where = $request->input('q');
$orderBy = $request->input('order');
$perPage = $request->input('per_page') ?? 5000;
$connection = env('DB_DATABASE_' . strtoupper($app));

// Base query with parameter placeholders
$query = DB::connection($connection)->table(strtolower($tableName));

// Add WHERE clause if provided
if(isset($where)) {
foreach ($where as $condition) {
// Extract the column name, operator, and value from the condition
$columnName = $condition['column'];
$operator = $condition['operator'];
$value = $condition['value'];

// Add the where condition to the query
$query->where($columnName, $operator, $value);
}
}

// Add ORDER BY clause if provided
if(isset($orderBy)) {
$orderBy = explode("~", $orderBy);
if(sizeof($orderBy) === 2) {
$query->orderBy($orderBy[0], $orderBy[1]);
}
}

// pagination parameters
$currentPage = $request->input('page', 1); // Current page, default is 1
$offset = ($currentPage - 1) * $perPage;

// append LIMIT and OFFSET to the query
$query->limit($perPage)->offset($offset);

// Execute the query with parameter bindings
try {
$data = $query->get();
} catch (\Exception $exception) {
return response()->json(['status' => false, 'body' => $exception->errorInfo[0]]);
}

// Fetch total count for pagination
$totalCountQuery = "SELECT COUNT(*) AS total FROM " . strtolower($tableName);
$totalCount = DB::connection($connection)->selectOne($totalCountQuery);

// Create pagination object
$paginatedData = new LengthAwarePaginator(
$data, $totalCount->total, $perPage, $currentPage
);


return response()->json(['status' => true, 'body' => $paginatedData]);
}

public function fetchTables(Request $request)
{
$app = $request->input('app');

try {
$tables = DB::connection(env('DB_DATABASE_' . strtoupper($app)))
->select("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema='public'");
} catch (\Exception $exception) {
return response()->json(['status' => false, 'body' => $exception->errorInfo[0]], 200);
}

return Response::json(['status' => true, 'body' => $tables], 200);
}

public function fetchColumns(Request $request)
{
$tableName = $request->input('table');
$app = $request->input('app');
try {
$columns = Schema::connection(env('DB_DATABASE_' . strtoupper($app)))
->getColumns(strtolower($tableName));
} catch (\Exception $exception) {
return response()->json(['status' => false, 'body' => $exception->errorInfo[0]], 200);
}

return Response::json(['status' => true, 'body' => $columns], 200);
}
}
8 changes: 8 additions & 0 deletions app/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class Kernel extends HttpKernel
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

'api2' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\App\Http\Middleware\Api2Middleware::class,
],
];

/**
Expand Down Expand Up @@ -72,5 +77,8 @@ class Kernel extends HttpKernel
'institution_admin' => \Modules\Institution\App\Http\Middleware\IsAdmin::class,
'super_admin' => \App\Http\Middleware\SuperAdmin::class,

'apiauth' => \App\Http\Middleware\ApiAuth::class,


];
}
20 changes: 20 additions & 0 deletions app/Http/Middleware/Api2Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class Api2Middleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
}
67 changes: 67 additions & 0 deletions app/Http/Middleware/ApiAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace App\Http\Middleware;

use Closure;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Illuminate\Http\Request;
use Response;

class ApiAuth
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$roles
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$roles)
{
$forwardedHost = $request->headers->get('X-Forwarded-Host');

// Prevent access to the API except via the gateway
if ($forwardedHost !== env('KEYCLOAK_APS_URL')) {
return Response::json(['error' => 'Unauthorized.' . $forwardedHost . ',,,' . env('KEYCLOAK_APS_URL')], 403);
}

$token = request()->bearerToken();
if(is_null($token)){
return Response::json(['status' => false, 'error' => 'Missing token.'], 401);
}
$jwksUri = env('KEYCLOAK_APS_ISS') . env('KEYCLOAK_APS_CERT_PATH');
$jwksJson = file_get_contents($jwksUri);
$jwksData = json_decode($jwksJson, true);
$matchingKey = null;
foreach ($jwksData['keys'] as $key) {
if (isset($key['use']) && $key['use'] === 'sig') {
$matchingKey = $key;
break;
}
}

$wrappedPk = wordwrap($matchingKey['x5c'][0], 64, "\n", true);
$pk = "-----BEGIN CERTIFICATE-----\n" . $wrappedPk . "\n-----END CERTIFICATE-----";

try {
$decoded = JWT::decode($token, new Key($pk, 'RS256'));
} catch (ExpiredException $e) {
return Response::json(['status' => false, 'error' => 'Token has expired.'], 401);
} catch (\Exception $e) {
return Response::json(['status' => false, 'error' => "An error occurred: " . $e->getMessage()], 401);
}

if(is_null($decoded)) {
return Response::json(['status' => false, 'error' => "Invalid token."], 401);
}else{
//only validate for accounts that we have registered
if($decoded->iss === env('KEYCLOAK_APS_ISS')){
return $next($request);
}
}

return Response::json(['status' => false, 'error' => "Generic error."], 403);
}
}
4 changes: 4 additions & 0 deletions app/Providers/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public function boot(): void
});

$this->routes(function () {
Route::prefix('api2')
->middleware('api2')
->group(base_path('routes/api2.php'));

Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Expand Down
21 changes: 21 additions & 0 deletions routes/api2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use App\Http\Controllers\ServiceAccountController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(['middleware' => 'apiauth'], function () {
Route::post('/fetch', [ServiceAccountController::class, 'fetchData']);
Route::post('/tables', [ServiceAccountController::class, 'fetchTables']);
Route::post('/columns', [ServiceAccountController::class, 'fetchColumns']);
});