Skip to content

Commit

Permalink
Laravel 11 (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
nie7321 authored Mar 18, 2024
1 parent e727e79 commit 14cb2f0
Show file tree
Hide file tree
Showing 17 changed files with 134 additions and 93 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

strategy:
matrix:
php-versions: ['8.1', '8.2', '8.3']
php-versions: ['8.2', '8.3']

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/vendor/
/.phpunit.result.cache
/.phpunit.cache

# Logs
logs
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## [v11.0.0] - 2024-03-18
### Changed
- Dropped support for PHP 8.1.
- Support for Laravel 11 has been added

The route stub has been updated for the new Laravel 11 skeleton. When upgrading, the `withoutMiddleware()` call on the Azure AD callback route must be changed to exclude the `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` class, since the new Laravel skeleton no longer ships with `App\Http\Middleware\VerifyCsrfToken`.

## [v10.0.0] - 2024-02-06
### Added
- The `eventhub:dlq:restore-messages` artisan command has been added. This is a tool to move messages from the DLQ back to the original queue for re-processing.
Expand Down
16 changes: 9 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
"northwestern university",
"laravel"
],
"authors": [{
"name": "Nicholas Evans",
"email": "nick.evans@northwestern.edu"
}],
"authors": [
{
"name": "Nicholas Evans",
"email": "nick.evans@northwestern.edu"
}
],
"require": {
"php": ">=8.1",
"php": ">=8.2",
"guzzlehttp/guzzle": "^7.0|^6.0",
"northwestern-sysdev/event-hub-php-sdk": "^3.0",
"laravel/ui": "^3.0|^2.0|^4.0",
Expand All @@ -22,9 +24,9 @@
"firebase/php-jwt": "^5.3"
},
"require-dev": {
"orchestra/testbench": "~8.0",
"orchestra/testbench": "~9.0",
"php-coveralls/php-coveralls": "^2.4",
"phpunit/phpunit": "^9.0",
"phpunit/phpunit": "^10.0",
"laravel/pint": "^1.13",
"larastan/larastan": "^2.0"
},
Expand Down
18 changes: 18 additions & 0 deletions docs/upgrading.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Upgrading

## From v10 to v11
When upgrading to Laravel 11 from a previous version, if you have applied the Laravel skeleton simplifications, you will need to update the Azure AD callback route when deleting the `\App\Http\Middleware\VerifyCsrfToken`:

```diff
diff --git a/stubs/routes.stub b/stubs/routes.stub
index 34d4712..c65c785 100644
--- a/stubs/routes.stub
+++ b/stubs/routes.stub
@@ -5,6 +5,6 @@ Route::get('auth/logout', [\App\Controllers\Auth\WebSSOController::class, 'logou
Route::group(['prefix' => 'auth/azure-ad'], function () {
Route::get('redirect', [\App\Controllers\Auth\WebSSOController::class, 'oauthRedirect'])->name('login-oauth-redirect');
Route::post('callback', [\App\Controllers\Auth\WebSSOController::class, 'oauthCallback'])->name('login-oauth-callback')
- ->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
+ ->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class]);
Route::post('oauth-logout', [\App\Controllers\Auth\WebSSOController::class, 'oauthLogout'])->name('login-oauth-logout');
});
```

## From v9 to v10
PHP 7.4 & 8.0 support has been dropped.

Expand Down
14 changes: 7 additions & 7 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage includeUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Test Suite">
<directory suffix=".php">./tests</directory>
<directory suffix=".php">./tests/Feature</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
2 changes: 1 addition & 1 deletion stubs/routes.stub
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ Route::get('auth/logout', [\App\Controllers\Auth\WebSSOController::class, 'logou
Route::group(['prefix' => 'auth/azure-ad'], function () {
Route::get('redirect', [\App\Controllers\Auth\WebSSOController::class, 'oauthRedirect'])->name('login-oauth-redirect');
Route::post('callback', [\App\Controllers\Auth\WebSSOController::class, 'oauthCallback'])->name('login-oauth-callback')
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class]);
Route::post('oauth-logout', [\App\Controllers\Auth\WebSSOController::class, 'oauthLogout'])->name('login-oauth-logout');
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?php

namespace Northwestern\SysDev\SOA\Tests\Auth\Entity;
namespace Northwestern\SysDev\SOA\Tests\Feature\Auth\Entity;

use Northwestern\SysDev\SOA\Auth\Entity\ActiveDirectoryUser;
use Orchestra\Testbench\TestCase;

class ActiveDirectoryUserTest extends TestCase
final class ActiveDirectoryUserTest extends TestCase
{
public function testEntity()
public function testEntity(): void
{
$user = new ActiveDirectoryUser('abcdefg', [
'mailNickname' => 'TEST123',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Northwestern\SysDev\SOA\Tests\Auth;
namespace Northwestern\SysDev\SOA\Tests\Feature\Auth;

use GuzzleHttp\Exception\ClientException;
use Illuminate\Auth\Authenticatable;
Expand All @@ -18,11 +18,11 @@
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class OAuthAuthenticationTest extends TestCase
final class OAuthAuthenticationTest extends TestCase
{
const OAUTH_DUMMY_PROVIDER_URL = 'https://oauth.example.org';

public function test_redirects_to_oauth_provider()
public function test_redirects_to_oauth_provider(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
return $this->mock_controller()->oauthRedirect($request);
Expand All @@ -32,7 +32,7 @@ public function test_redirects_to_oauth_provider()
$response->assertRedirect(self::OAUTH_DUMMY_PROVIDER_URL);
}

public function test_callback_success()
public function test_callback_success(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
$oauthUser = $this->createStub(User::class);
Expand All @@ -52,12 +52,11 @@ public function test_callback_success()
$this->assertAuthenticated();
}

/**
* @dataProvider restartableExceptionProvider
*/
public function test_exceptions_restart_flow($exception)
public function test_exceptions_restart_flow_for_invalid_state(): void
{
$exception = new InvalidStateException;
$this->app['router']->get('/login-oauth-redirect', function () {
//
})->name('login-oauth-redirect');

$this->app['router']->get(__METHOD__, function (Request $request) use ($exception) {
Expand All @@ -71,24 +70,33 @@ public function test_exceptions_restart_flow($exception)
$response->assertRedirect('/login-oauth-redirect');
}

public function restartableExceptionProvider()
public function test_exceptions_restart_flow_for_guzzle_400_code(): void
{
$errorResponse = $this->createStub(ResponseInterface::class);
$errorResponse->method('getStatusCode')->willReturn(400);

return [
'invalid state' => [new InvalidStateException],
'guzzle 400 w/ message' => [
new ClientException(
'OAuth2 Authorization code was already redeemed',
$this->createStub(RequestInterface::class),
$errorResponse
),
],
];
$exception = new ClientException(
'OAuth2 Authorization code was already redeemed',
$this->createStub(RequestInterface::class),
$errorResponse
);

$this->app['router']->get('/login-oauth-redirect', function () {
//
})->name('login-oauth-redirect');

$this->app['router']->get(__METHOD__, function (Request $request) use ($exception) {
$driver = $this->createStub(AzureDriver::class);
$driver->method('user')->willThrowException($exception);

return $this->mock_controller($driver)->oauthCallback($request);
});

$response = $this->get(__METHOD__);
$response->assertRedirect('/login-oauth-redirect');
}

public function test_unhandled_exceptions_are_rethrown()
public function test_unhandled_exceptions_are_rethrown(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
$driver = $this->createStub(AzureDriver::class);
Expand All @@ -101,7 +109,7 @@ public function test_unhandled_exceptions_are_rethrown()
$this->assertEquals('Unhandled, yay!', $response->exception->getMessage());
}

public function test_logout()
public function test_logout(): void
{
$this->app['router']->post(__METHOD__, function (Request $request) {
$driver = $this->createStub(AzureDriver::class);
Expand All @@ -116,7 +124,7 @@ public function test_logout()
$this->assertStringContainsString('/oauth2/v2.0/logout', $response->headers->get('Location'));
}

public function test_logout_with_redirect()
public function test_logout_with_redirect(): void
{
$this->app['router']->post(__METHOD__, function (Request $request) {
$driver = $this->createStub(AzureDriver::class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Northwestern\SysDev\SOA\Tests;
namespace Northwestern\SysDev\SOA\Tests\Feature\Auth;

use GuzzleHttp\Client;
use Illuminate\Auth\Authenticatable;
Expand All @@ -13,10 +13,11 @@
use Northwestern\SysDev\SOA\Exceptions\InsecureSsoError;
use Northwestern\SysDev\SOA\Providers\NuSoaServiceProvider;
use Northwestern\SysDev\SOA\Tests\Concerns\TestsOpenAM11;
use Northwestern\SysDev\SOA\Tests\TestCase;
use Northwestern\SysDev\SOA\WebSSO;
use Northwestern\SysDev\SOA\WebSSOImpl\ApigeeAgentless;

class OpenAM11AuthenticationTest extends TestCase
final class OpenAM11AuthenticationTest extends TestCase
{
use TestsOpenAM11;

Expand All @@ -26,7 +27,7 @@ class OpenAM11AuthenticationTest extends TestCase

protected $strategy;

public function setUp(): void
protected function setUp(): void
{
parent::setUp();

Expand All @@ -48,7 +49,7 @@ protected function getEnvironmentSetUp($app)
$app['config']->set('duo.enabled', false);
} // end getEnvironmentSetUp

public function test_successful_login_no_mfa()
public function test_successful_login_no_mfa(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
// Doing withCookie() on the get won't work cuz that only injects into the Request,
Expand All @@ -67,7 +68,7 @@ public function test_successful_login_no_mfa()
$this->assertAuthenticated();
}

public function test_redirects_when_no_cookie()
public function test_redirects_when_no_cookie(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
unset($_COOKIE['nusso']);
Expand All @@ -79,7 +80,7 @@ public function test_redirects_when_no_cookie()
$this->assertSsoRedirect($response);
}

public function test_redirects_when_cookie_is_invalid()
public function test_redirects_when_cookie_is_invalid(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
$_COOKIE['nusso'] = 'dummy-token';
Expand All @@ -93,7 +94,7 @@ public function test_redirects_when_cookie_is_invalid()
$this->assertSsoRedirect($response);
}

public function test_exception_when_apigee_key_is_invalid()
public function test_exception_when_apigee_key_is_invalid(): void
{
$this->app['router']->get(__METHOD__, function (Request $request) {
$_COOKIE['nusso'] = 'dummy-token';
Expand All @@ -106,7 +107,7 @@ public function test_exception_when_apigee_key_is_invalid()
$this->get(__METHOD__)->assertStatus(500);
}

public function test_sends_to_mfa()
public function test_sends_to_mfa(): void
{
$this->app['config']->set('nusoa.sso.authTree', 'ldap-and-duo');
$this->app['config']->set('duo.enabled', true);
Expand All @@ -127,7 +128,7 @@ public function test_sends_to_mfa()
$this->assertGreaterThan(-1, strpos($response->getTargetUrl(), 'authIndexValue=ldap-and-duo'), $error);
}

public function tests_exception_when_insecure_connection_used()
public function tests_exception_when_insecure_connection_used(): void
{
// Disable the "force HTTPS" thing in ::prepareUrlForRequest
$this->useSecure = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
<?php

namespace Northwestern\SysDev\SOA\Tests;
namespace Northwestern\SysDev\SOA\Tests\Feature;

use Northwestern\SysDev\SOA\DirectorySearch;
use Northwestern\SysDev\SOA\Tests\TestCase;

class DirectoySearchTest extends TestCase
final class DirectoySearchTest extends TestCase
{
protected $service = DirectorySearch::class;

public function testGoodLookup()
public function testGoodLookup(): void
{
$this->api->setHttpClient($this->mockedResponse(200, '{"results":[{ "displayName" : [ "Test E User" ], "givenName" : [ "Test" ], "sn" : [ "User" ], "eduPersonNickname" : [ "test" ], "mail" : "test@example.org", "nuStudentEmail" : "", "title" : [ "Tester" ], "telephoneNumber" : "123 1231234", "nuTelephoneNumber2" : "", "nuTelephoneNumber3" : "", "nuOtherTitle" : "" }]}'));

$info = $this->api->lookupByNetId('test', 'public');
$this->assertArrayHasKey('mail', $info);
} // end testGoodLookup

public function testBadLookup()
public function testBadLookup(): void
{
$this->api->setHttpClient($this->mockedResponse(404, '{"errorCode":404,"errorMessage":"No Data Found for = uid=test"}'));

$this->assertFalse($this->api->lookupByNetId('test', 'public'));
$this->assertNotEmpty($this->api->getLastError());
} // end testBadLookup

public function testBadPerms()
public function testBadPerms(): void
{
$this->api->setHttpClient($this->mockedResponse(401, '{"fault":{"faultstring":"Invalid ApiKey for given resource","detail":{"errorcode":"oauth.v2.InvalidApiKeyForGivenResource"}}}'));

$this->assertFalse($this->api->lookupByNetId('test', 'public'));
$this->assertNotEmpty($this->api->getLastError());
} // end testBadPerms

public function testBadApiKey()
public function testBadApiKey(): void
{
$this->api->setHttpClient($this->mockedResponse(401, '{"fault":{"faultstring":"Failed to resolve API Key variable request.header.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}'));

$this->assertFalse($this->api->lookupByNetId('test', 'public'));
$this->assertNotEmpty($this->api->getLastError());
} // end testBadApiKey

public function testConnectionFailure()
public function testConnectionFailure(): void
{
$this->api->setHttpClient($this->mockedConnError());

Expand Down
Loading

0 comments on commit 14cb2f0

Please sign in to comment.