Skip to content

Commit 76db396

Browse files
authored
Merge pull request #55 from IFRCGo/staging
Staging
2 parents 0f435ad + 58e7297 commit 76db396

7 files changed

Lines changed: 232 additions & 26 deletions

File tree

app/Http/Controllers/ApplicationController.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,138 @@ public function delete($id)
360360
'message' => 'Application deleted',
361361
], 200);
362362
}
363+
364+
/**
365+
* @OA\Patch(
366+
* path="/apps/{id}/activate",
367+
* tags={"Applications"},
368+
* summary="Activate an application by ID",
369+
* operationId="activateApplication",
370+
* security={},
371+
* deprecated=true,
372+
* @OA\Parameter(
373+
* name="id",
374+
* in="path",
375+
* required=true,
376+
* description="ID of the application to activate",
377+
* @OA\Schema(type="integer", format="int64")
378+
* ),
379+
* @OA\Response(
380+
* response=200,
381+
* description="Successful response",
382+
* @OA\JsonContent(
383+
* type="object",
384+
* @OA\Property(property="data", type="array", @OA\Items(type="object"))
385+
* )
386+
* )
387+
* )
388+
*/
389+
public function activate($id)
390+
{
391+
try {
392+
$application = $this->repo->find($id);
393+
} catch (\Exception $e) {
394+
Log::error('Application not found', ['message' => $e->getMessage()]);
395+
396+
return response()->json([
397+
'status' => 404,
398+
'error_message' => 'Application does not exist',
399+
'errors' => ['No matching Application'],
400+
], 404);
401+
}
402+
403+
if ($application->tenant_id !== $this->tenantId) {
404+
return response()->json([
405+
'status' => 403,
406+
'error_message' => 'Application does not belong to tenant',
407+
'errors' => ['Application does not belong to tenant'],
408+
], 403);
409+
}
410+
411+
try {
412+
$this->repo->updateWithIdAndInput($id, ['is_active' => true]);
413+
} catch (\Exception $e) {
414+
Log::error('Application not activated', ['message' => $e->getMessage()]);
415+
416+
return response()->json([
417+
'status' => 500,
418+
'error_message' => 'Unable to activate Application',
419+
'errors' => [$e->getMessage()],
420+
], 500);
421+
}
422+
423+
$application = $this->repo->find($id);
424+
425+
$resource = new \League\Fractal\Resource\Item($application, new ApplicationTransformer());
426+
$response = $this->manager->createData($resource);
427+
428+
return response()->json($response->toArray(), 200);
429+
}
430+
431+
/**
432+
* @OA\Patch(
433+
* path="/apps/{id}/deactivate",
434+
* tags={"Applications"},
435+
* summary="Deactivate an application by ID",
436+
* operationId="deactivateApplication",
437+
* security={},
438+
* deprecated=true,
439+
* @OA\Parameter(
440+
* name="id",
441+
* in="path",
442+
* required=true,
443+
* description="ID of the application to deactivate",
444+
* @OA\Schema(type="integer", format="int64")
445+
* ),
446+
* @OA\Response(
447+
* response=200,
448+
* description="Successful response",
449+
* @OA\JsonContent(
450+
* type="object",
451+
* @OA\Property(property="data", type="array", @OA\Items(type="object"))
452+
* )
453+
* )
454+
* )
455+
*/
456+
public function deactivate($id)
457+
{
458+
try {
459+
$application = $this->repo->find($id);
460+
} catch (\Exception $e) {
461+
Log::error('Application not found', ['message' => $e->getMessage()]);
462+
463+
return response()->json([
464+
'status' => 404,
465+
'error_message' => 'Application does not exist',
466+
'errors' => ['No matching Application'],
467+
], 404);
468+
}
469+
470+
if ($application->tenant_id !== $this->tenantId) {
471+
return response()->json([
472+
'status' => 403,
473+
'error_message' => 'Application does not belong to tenant',
474+
'errors' => ['Application does not belong to tenant'],
475+
], 403);
476+
}
477+
478+
try {
479+
$this->repo->updateWithIdAndInput($id, ['is_active' => false]);
480+
} catch (\Exception $e) {
481+
Log::error('Application not deactivated', ['message' => $e->getMessage()]);
482+
483+
return response()->json([
484+
'status' => 500,
485+
'error_message' => 'Unable to deactivate Application',
486+
'errors' => [$e->getMessage()],
487+
], 500);
488+
}
489+
490+
$application = $this->repo->find($id);
491+
492+
$resource = new \League\Fractal\Resource\Item($application, new ApplicationTransformer());
493+
$response = $this->manager->createData($resource);
494+
495+
return response()->json($response->toArray(), 200);
496+
}
363497
}

app/Http/Middleware/ApiAuthMiddleware.php

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use App\Models\UsageLog;
77
use Carbon\Carbon;
88
use Closure;
9-
use Illuminate\Support\Facades\Log;
109

1110
class ApiAuthMiddleware extends BasicAuthMiddleware
1211
{
@@ -19,22 +18,40 @@ class ApiAuthMiddleware extends BasicAuthMiddleware
1918
*/
2019
public function handle($request, Closure $next)
2120
{
22-
$application = Application::where('key', '=', $request->header('x-api-key'))->first();
21+
$apiKey = $request->header('x-api-key');
22+
$authHeader = $request->header('Authorization');
23+
$isBasicAuth = $authHeader && str_starts_with($authHeader, 'Basic ');
2324

24-
if (! $application) {
25+
if (!$apiKey && !$isBasicAuth) {
26+
return response()->json(['error' => 'Authentication required. Provide API key or Basic auth'], 401);
27+
}
28+
29+
if ($isBasicAuth) {
2530
return parent::handle($request, $next);
2631
}
27-
$usageLog = new UsageLog;
28-
$usageLog->application_id = $application->id;
29-
$usageLog->method = $request->method();
30-
$usageLog->endpoint = $request->path();
31-
$usageLog->timestamp = Carbon::now()->toDateTimeString();
32-
$usageLog->code_status = 200;
33-
$usageLog->language = $request->input('language', false) ? $request->input('language', null) : $request->header('Accept-Language', null);
34-
$usageLog->subnational = $request->input('subnational', null);
35-
$usageLog->event_type = $request->input('eventType', null);
36-
$usageLog->save();
37-
$request->usageLog=$usageLog;
32+
33+
if ($apiKey) {
34+
$application = Application::where('key', '=', $apiKey)->first();
35+
36+
if (!$application) {
37+
return response()->json(['error' => 'Invalid API key'], 401);
38+
}
39+
40+
if (!$application->is_active) {
41+
return response()->json(['error' => 'Application is inactive'], 403);
42+
}
43+
$usageLog = new UsageLog;
44+
$usageLog->application_id = $application->id;
45+
$usageLog->method = $request->method();
46+
$usageLog->endpoint = $request->path();
47+
$usageLog->timestamp = Carbon::now()->toDateTimeString();
48+
$usageLog->code_status = 200;
49+
$usageLog->language = $request->input('language', false) ? $request->input('language', null) : $request->header('Accept-Language', null);
50+
$usageLog->subnational = $request->input('subnational', null);
51+
$usageLog->event_type = $request->input('eventType', null);
52+
$usageLog->save();
53+
$request->usageLog = $usageLog;
54+
}
3855

3956
return $next($request);
4057
}

app/Models/Application.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Application extends Model
1616
*/
1717
protected $table = 'applications';
1818

19-
public $timestamps = false;
19+
public $timestamps = true;
2020

2121
/**
2222
* The attributes that are mass assignable.
@@ -30,10 +30,31 @@ class Application extends Model
3030
'description',
3131
'estimated_users_count',
3232
'key',
33+
'is_active',
3334
];
3435

3536
protected $dates = ['deleted_at'];
3637

38+
/**
39+
* The attributes that should be cast to native types.
40+
*
41+
* @var array
42+
*/
43+
protected $casts = [
44+
'is_active' => 'boolean',
45+
];
46+
47+
/**
48+
* Scope to get only active applications
49+
*
50+
* @param \Illuminate\Database\Eloquent\Builder $query
51+
* @return \Illuminate\Database\Eloquent\Builder
52+
*/
53+
public function scopeActive($query)
54+
{
55+
return $query->where('is_active', true);
56+
}
57+
3758
/**
3859
* A sure method to generate a unique API key
3960
*
@@ -42,7 +63,7 @@ class Application extends Model
4263
public static function generateKey()
4364
{
4465
do {
45-
$newKey = str_random(32);
66+
$newKey = \Illuminate\Support\Str::random(32);
4667
} // Already in the DB? Fail. Try again
4768
while (self::keyExists($newKey));
4869

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class AddIsActiveToAplicationsTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::table('applications', function (Blueprint $table) {
17+
$table->boolean('is_active')->default(true)->after('key');
18+
});
19+
}
20+
21+
/**
22+
* Reverse the migrations.
23+
*
24+
* @return void
25+
*/
26+
public function down()
27+
{
28+
Schema::table('applications', function (Blueprint $table) {
29+
$table->dropColumn('is_active');
30+
});
31+
}
32+
}

docker/Dockerfile

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
#
21
FROM php:7.4-apache
32

4-
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
5-
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
6-
RUN php composer-setup.php
7-
RUN php -r "unlink('composer-setup.php');"
8-
RUN mv composer.phar /usr/local/bin/composer
3+
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
94

105
RUN apt update && apt install -y \
116
libzip-dev \
@@ -20,16 +15,13 @@ RUN docker-php-ext-install \
2015
COPY ./docker/vhost.conf /etc/apache2/sites-available/000-default.conf
2116
COPY ./ /var/www/html
2217

23-
# Install Composer dependencies
2418
WORKDIR /var/www/html
2519
RUN composer install --no-dev --prefer-dist --optimize-autoloader
2620

27-
# Generate Swagger documentation
2821
RUN php artisan l5-swagger:generate
2922

3023
RUN mkdir -p storage/framework/sessions
3124
RUN mkdir -p storage/framework/views
3225
RUN mkdir -p storage/framework/cache
3326
RUN chmod -R 775 storage
3427
RUN chown -R www-data:www-data storage
35-

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,9 @@
1818
"sass": "^1.15.2",
1919
"sass-loader": "^7.1.0",
2020
"vue-template-compiler": "^2.7.16"
21+
},
22+
"overrides": {
23+
"cipher-base": "^1.0.5",
24+
"sha.js": "^2.4.12"
2125
}
2226
}

routes/api.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@
6464
Route::delete('apps/{id}', 'ApplicationController@delete');
6565
Route::patch('apps/{id}', 'ApplicationController@update');
6666

67+
// Routes for activating/deactivating applications
68+
Route::patch('apps/{id}/activate', 'ApplicationController@activate');
69+
Route::patch('apps/{id}/deactivate', 'ApplicationController@deactivate');
70+
Route::get('admin/apps', 'ApplicationController@getAllForAdmin');
71+
6772
// Usage log endpoints
6873
Route::get('usage/applications', 'UsageLogController@getApplicationLogs');
6974
Route::get('usage/endpoints', 'UsageLogController@getEndpointLogs');
@@ -95,4 +100,5 @@
95100
'error' => 'API version v1 is no longer supported. Please use /v2/.'
96101
], 410);
97102
})->where('any', '.*');
98-
});
103+
});
104+

0 commit comments

Comments
 (0)