Skip to content

Commit 8e8cc48

Browse files
committed
feat: integrate Keycloak authentication and add Transcript GraphQL access
1 parent 328dd15 commit 8e8cc48

22 files changed

Lines changed: 3195 additions & 1463 deletions

.github/workflows/build-pr.yml

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,39 @@ on:
88

99
jobs:
1010
laravel-tests:
11-
runs-on: ubuntu-24.04
11+
runs-on: ubuntu-latest
12+
environment: Testing
1213

1314
steps:
1415
- name: Checkout
15-
uses: actions/checkout@v3
16+
uses: actions/checkout@v4
17+
- name: Setup PHP
18+
uses: shivammathur/setup-php@v2
1619
with:
17-
# Make sure the actual branch is checked out when running on pull requests
18-
ref: ${{ github.head_ref }}
19-
# This is important to fetch the changes to the previous commit
20-
fetch-depth: 0
21-
- uses: shivammathur/setup-php@v2
20+
php-version: '8.4'
21+
- name: Install pnpm
22+
uses: pnpm/action-setup@v4
23+
- name: Setup Node.js & pnpm
24+
uses: actions/setup-node@v4
2225
with:
23-
php-version: '8.3'
24-
- name: Use Node.js ${{ matrix.node-version }}
25-
uses: actions/setup-node@v3
26+
node-version: lts/*
27+
cache: 'pnpm'
28+
- name: Cache Composer dependencies
29+
uses: actions/cache@v4
2630
with:
27-
node-version: 22.x
28-
- uses: pnpm/action-setup@v2
29-
name: Install pnpm
30-
with:
31-
version: 10
32-
run_install: false
33-
- name: Get pnpm store directory
34-
shell: bash
35-
run: |
36-
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
37-
- uses: actions/cache@v3
38-
name: Setup pnpm cache
39-
with:
40-
path: ${{ env.STORE_PATH }}
41-
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
31+
path: vendor
32+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
4233
restore-keys: |
43-
${{ runner.os }}-pnpm-store-
34+
${{ runner.os }}-composer-
4435
- name: Copy .env
4536
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
46-
- name: Install Dependencies
37+
- name: Install PHP Dependencies
4738
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
48-
- name: Generate key
39+
- name: Generate Laravel key
4940
run: php artisan key:generate
50-
- name: Directory Permissions
41+
- name: Set Directory Permissions
5142
run: chmod -R 777 storage bootstrap/cache
52-
- name: Create Database
53-
run: |
54-
mkdir -p database
55-
touch database/database.sqlite
56-
- name: Install JS Dependencies
57-
run: pnpm install
58-
- name: Build JS assets
59-
run: pnpm run build
60-
- name: Execute tests
61-
env:
62-
DB_CONNECTION: sqlite
63-
DB_DATABASE: database/database.sqlite
64-
run: php artisan test
43+
- name: Install & Build JS
44+
run: pnpm install && pnpm run build
45+
- name: Run Tests
46+
run: ./vendor/bin/pest --parallel

app/Exceptions/Handler.php

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace App\GraphQL\Types\User;
4+
5+
use App\Models\User;
6+
7+
final readonly class Transcript {
8+
public function __invoke(User $user, array $args): array {
9+
return $user->getActivityTranscript()->toArray();
10+
}
11+
}

app/Models/Project.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use App\ProjectClosureStatus;
77
use Carbon\Carbon;
88
use Illuminate\Database\Eloquent\Builder;
9+
use Illuminate\Database\Eloquent\Casts\Attribute;
910
use Illuminate\Database\Eloquent\Collection;
1011
use Illuminate\Database\Eloquent\Factories\HasFactory;
1112
use Illuminate\Database\Eloquent\Model;
@@ -122,6 +123,10 @@ public function getNumber(): string {
122123
return $this->year . '-' . $this->number;
123124
}
124125

126+
public function identifier(): Attribute {
127+
return Attribute::make(fn() => "{$this->year}-{$this->number}");
128+
}
129+
125130
public static function latestOfYear(?int $year): ?self {
126131
return self::where('year', $year ?? (date('Y') + 543))->orderByDesc('number')->first();
127132
}

app/Models/User.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public function getActivityTranscript() {
116116
->sortByDesc('project.period_end')
117117
->map(function ($participant): array {
118118
return ($participant->project_type == 'App\Models\Project') ? [
119+
'id' => $participant->id,
119120
'identifier' => $participant->project->year.'-'.$participant->project->number,
120121
'project_id' => $participant->project->id,
121122
'name' => $participant->project->name,
@@ -127,6 +128,7 @@ public function getActivityTranscript() {
127128
'approve_status' => $participant->approve_status,
128129
'title' => $participant->title,
129130
] : [
131+
'id' => $participant->id,
130132
'identifier' => 'A'.$participant->project->id,
131133
'activity_id' => $participant->project->id,
132134
'name' => $participant->project->name,

app/Providers/AuthServiceProvider.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
use App\Models\Project;
77
use App\Models\User;
88
use Illuminate\Auth\Access\Response;
9+
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
910
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
11+
use Illuminate\Support\Facades\Auth;
1012
use Illuminate\Support\Facades\Gate;
1113

1214
class AuthServiceProvider extends ServiceProvider
@@ -57,7 +59,19 @@ public function boot()
5759
return in_array('faculty', $userRoles) or in_array('activity', $userRoles);
5860
});
5961
Gate::define('view-transcript', function (User $user) {
60-
return in_array('view_transcript', explode(',', $user->roles)) or $user->can('create-activity');
62+
return in_array('view_transcript', explode(',', $user->roles)) or $user->can('create-activity') or $user->can('admin-action');
63+
});
64+
65+
// API permissions
66+
Gate::define('api-access', function (AuthenticatableContract $user) {
67+
if ($user instanceof User) {
68+
return $user->can('view-transcript');
69+
}
70+
if ($user instanceof \KeycloakGuard\User) {
71+
return Auth::hasScope('students_activity');
72+
}
73+
74+
return false;
6175
});
6276
}
6377
}

bootstrap/app.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
use Illuminate\Foundation\Application;
44
use Illuminate\Foundation\Configuration\Exceptions;
55
use Illuminate\Foundation\Configuration\Middleware;
6+
use KeycloakGuard\Exceptions\TokenException;
7+
use Sentry\Laravel\Integration;
68

79
return Application::configure(basePath: dirname(__DIR__))
810
->withRouting(
911
web: __DIR__.'/../routes/web.php',
10-
commands: __DIR__.'/../routes/console.php',
1112
api: __DIR__.'/../routes/api.php',
13+
commands: __DIR__.'/../routes/console.php',
1214
health: '/up',
1315
)
1416
->withMiddleware(function (Middleware $middleware) {
@@ -20,5 +22,12 @@
2022
]);
2123
})
2224
->withExceptions(function (Exceptions $exceptions) {
23-
//
25+
$exceptions->render(function (TokenException $e) {
26+
return response()->json([
27+
'message' => $e->getMessage(),
28+
], 401);
29+
});
30+
if (app()->bound('sentry') and config('app.env') === 'production') {
31+
Integration::handles($exceptions);
32+
}
2433
})->create();

composer.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
"laravel/sanctum": "^4.0",
1919
"laravel/socialite": "^5.2",
2020
"laravel/tinker": "^2.7",
21+
"mll-lab/laravel-graphiql": "^4.0",
22+
"nuwave/lighthouse": "^6.63",
2123
"openpsa/ranger": "^0.5.2",
24+
"overtrue/laravel-keycloak-guard": "dev-master",
2225
"phpoffice/phpspreadsheet": "^5.0",
2326
"phpoffice/phpword": "^1.2",
2427
"sentry/sentry-laravel": "^4.4",
@@ -31,10 +34,16 @@
3134
"fakerphp/faker": "^1.9.1",
3235
"mockery/mockery": "^1.4.2",
3336
"nunomaduro/collision": "^8.1",
34-
"pestphp/pest": "^3.0",
35-
"pestphp/pest-plugin-laravel": "^3.0",
37+
"pestphp/pest": "^4.0",
38+
"pestphp/pest-plugin-laravel": "^4.0",
3639
"spatie/laravel-ignition": "^2.0"
3740
},
41+
"repositories": [
42+
{
43+
"type": "vcs",
44+
"url": "https://github.com/keenthekeen/laravel-keycloak-guard.git"
45+
}
46+
],
3847
"autoload": {
3948
"psr-4": {
4049
"App\\": "app/",

0 commit comments

Comments
 (0)