Skip to content
Open
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
5 changes: 5 additions & 0 deletions app/config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

use Cake\Http\Middleware\BodyParserMiddleware;
use App\Middleware\ForceJsonIfAcceptedMiddleware;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;
Expand Down Expand Up @@ -54,12 +55,16 @@
// BodyParserMiddleware will automatically parse JSON bodies, but we only
// want that for API transactions, so we only apply it to the /api scope.
$builder->registerMiddleware('bodyparser', new BodyParserMiddleware());
$builder->registerMiddleware('forceJsonIfAccepted', new ForceJsonIfAcceptedMiddleware());

/*
* Apply a middleware to the current route scope.
* Requires middleware to be registered through `Application::routes()` with `registerMiddleware()`
*/
$builder->setExtensions(['json']);
$builder->applyMiddleware('bodyparser');
$builder->applyMiddleware('forceJsonIfAccepted');

// Use setPass to make parameter show up as function parameter
// Model specific actions, which will usually have more specific URLs:
// Note that while the UI uses dashes in URL paths, because we use {model} for the generic
Expand Down
91 changes: 91 additions & 0 deletions app/src/Middleware/ForceJsonIfAcceptedMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
/**
* COmanage Registry Force Json If Accepted Middleware
*
* Portions licensed to the University Corporation for Advanced Internet
* Development, Inc. ("UCAID") under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* UCAID licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @link https://www.internet2.edu/comanage COmanage Project
* @package registry
* @since COmanage Registry v5.3.0
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

declare(strict_types=1);

namespace App\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class ForceJsonIfAcceptedMiddleware implements MiddlewareInterface
{
/**
* If the client indicates it accepts JSON (via route extension or Accept header),
* ensure the response advertises JSON unless it is already JSON.
*
* @since COmanage Registry v5.3.0
* @param \Psr\Http\Message\ServerRequestInterface $request Request
* @param \Psr\Http\Server\RequestHandlerInterface $handler Handler
* @return \Psr\Http\Message\ResponseInterface Response
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);

if (!$this->clientWantsJson($request)) {
return $response;
}

// If it's already JSON, leave it alone.
$contentType = strtolower($response->getHeaderLine('Content-Type'));
if ($contentType !== '' && str_contains($contentType, 'application/json')) {
return $response;
}

// Otherwise override to JSON.
return $response->withHeader('Content-Type', 'application/json; charset=UTF-8');
}

/**
* Determine whether the client indicates it wants JSON.
*
* Currently supports an explicit route extension of "json" and Accept headers
* including "application/json" and vendor-specific "+json" media types.
*
* @since COmanage Registry v5.3.0
* @param \Psr\Http\Message\ServerRequestInterface $request Request
* @return bool True if the client indicates JSON is acceptable/preferred
*/
private function clientWantsJson(ServerRequestInterface $request): bool
{
$params = $request->getAttribute('params');
if (is_array($params) && (($params['_ext'] ?? null) === 'json')) {
return true;
}

$accept = strtolower($request->getHeaderLine('Accept'));

if (str_contains($accept, 'application/json')) {
return true;
}

return preg_match('/application\/[^;,+]+\+json\b/', $accept) === 1;
}
}