Skip to content

Commit 45b5b7c

Browse files
committed
added scope requirement to operation object
1 parent 8f43775 commit 45b5b7c

File tree

3 files changed

+78
-15
lines changed

3 files changed

+78
-15
lines changed

src/Generator.php

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
use Illuminate\Foundation\Http\FormRequest;
66
use Illuminate\Routing\Route;
77
use Illuminate\Support\Str;
8+
use Illuminate\Support\Arr;
89
use phpDocumentor\Reflection\DocBlockFactory;
910
use ReflectionMethod;
1011

1112
class Generator
1213
{
14+
const SECURITY_DEFINITION_NAME = 'OAuth2';
1315
const OAUTH_TOKEN_PATH = '/oauth/token';
1416
const OAUTH_AUTHORIZE_PATH = '/oauth/authorize';
1517

@@ -20,18 +22,27 @@ class Generator
2022
protected $originalUri;
2123
protected $method;
2224
protected $action;
25+
protected $middleware;
26+
protected $docParser;
27+
protected $hasSecurityDefinitions;
2328

2429
public function __construct($config, $routeFilter = null)
2530
{
2631
$this->config = $config;
2732
$this->routeFilter = $routeFilter;
2833
$this->docParser = DocBlockFactory::createInstance();
34+
$this->hasSecurityDefinitions = false;
2935
}
3036

3137
public function generate()
3238
{
3339
$this->docs = $this->getBaseInfo();
3440

41+
if ($this->config['parseSecurity'] && $this->hasOauthRoutes()) {
42+
$this->docs['securityDefinitions'] = $this->generateSecurityDefinitions();
43+
$this->hasSecurityDefinitions = true;
44+
}
45+
3546
foreach ($this->getAppRoutes() as $route) {
3647
$this->originalUri = $uri = $this->getRouteUri($route);
3748
$this->uri = strip_optional_char($uri);
@@ -40,11 +51,11 @@ public function generate()
4051
continue;
4152
}
4253

43-
if ($this->config['parseSecurity'] && $this->isOauthRoute()) {
44-
$this->docs['securityDefinitions'] = $this->generateSecurityDefinitions();
45-
}
54+
$middleware = isset($route->getAction()['middleware']) ? $route->getAction()['middleware'] : [];
4655

4756
$this->action = $route->getAction()['uses'];
57+
$this->middleware = $this->formatMiddleware($middleware);
58+
4859
$methods = $route->methods();
4960

5061
if (!isset($this->docs['paths'][$this->uri])) {
@@ -118,22 +129,22 @@ protected function generateSecurityDefinitions()
118129
$this->validateAuthFlow($authFlow);
119130

120131
$securityDefinition = [
121-
'OAuth2' => [
132+
self::SECURITY_DEFINITION_NAME => [
122133
'type' => 'oauth2',
123134
'flow' => $authFlow,
124135
]
125136
];
126137

127138

128139
if (in_array($authFlow, ['implicit', 'accessCode'])) {
129-
$securityDefinition['OAuth2']['authorizationUrl'] = $this->getEndpoint(self::OAUTH_AUTHORIZE_PATH);
140+
$securityDefinition[self::SECURITY_DEFINITION_NAME]['authorizationUrl'] = $this->getEndpoint(self::OAUTH_AUTHORIZE_PATH);
130141
}
131142

132143
if (in_array($authFlow, ['password', 'application', 'accessCode'])) {
133-
$securityDefinition['OAuth2']['tokenUrl'] = $this->getEndpoint(self::OAUTH_TOKEN_PATH);
144+
$securityDefinition[self::SECURITY_DEFINITION_NAME]['tokenUrl'] = $this->getEndpoint(self::OAUTH_TOKEN_PATH);
134145
}
135146

136-
$securityDefinition['OAuth2']['scopes'] = $this->generateOauthScopes();
147+
$securityDefinition[self::SECURITY_DEFINITION_NAME]['scopes'] = $this->generateOauthScopes();
137148

138149
return $securityDefinition;
139150
}
@@ -157,6 +168,10 @@ protected function generatePath()
157168
];
158169

159170
$this->addActionParameters();
171+
172+
if ($this->hasSecurityDefinitions) {
173+
$this->addActionScopes();
174+
}
160175
}
161176

162177
protected function addActionParameters()
@@ -176,6 +191,17 @@ protected function addActionParameters()
176191
}
177192
}
178193

194+
protected function addActionScopes()
195+
{
196+
foreach ($this->middleware as $middleware) {
197+
if ($middleware['name'] === 'scope' || $middleware['name'] === 'scopes') {
198+
$this->docs['paths'][$this->uri][$this->method]['security'] = [
199+
self::SECURITY_DEFINITION_NAME => $middleware['parameters']
200+
];
201+
}
202+
}
203+
}
204+
179205
protected function getFormRules()
180206
{
181207
if (!is_string($this->action)) {
@@ -237,9 +263,20 @@ private function isFilteredRoute()
237263
return !preg_match('/^' . preg_quote($this->routeFilter, '/') . '/', $this->uri);
238264
}
239265

240-
private function isOauthRoute()
266+
/**
267+
* Assumes routes have been created using Passport::routes()
268+
*/
269+
private function hasOauthRoutes()
241270
{
242-
return $this->uri === self::OAUTH_TOKEN_PATH || $this->uri === self::OAUTH_AUTHORIZE_PATH;
271+
foreach ($this->getAppRoutes() as $route) {
272+
$uri = $this->getRouteUri($route);
273+
274+
if ($uri === self::OAUTH_TOKEN_PATH || $uri === self::OAUTH_AUTHORIZE_PATH) {
275+
return true;
276+
}
277+
}
278+
279+
return false;
243280
}
244281

245282
private function getEndpoint(string $path)
@@ -264,4 +301,17 @@ private function validateAuthFlow(string $flow)
264301
throw new LaravelSwaggerException('Invalid OAuth flow passed');
265302
}
266303
}
304+
305+
private function formatMiddleware($middleware)
306+
{
307+
$middleware = Arr::wrap($middleware);
308+
309+
return array_map(function($mw) {
310+
$tokens = explode(':', $mw, 2);
311+
$name = $tokens[0];
312+
$parameters = isset($tokens[1]) ? explode(',', $tokens[1]) : [];
313+
314+
return compact('name', 'parameters');
315+
}, $middleware);
316+
}
267317
}

tests/GeneratorTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,15 @@ public function testRouteData($paths)
220220
$this->assertEquals('', $paths['/users/details']['get']['description']);
221221
}
222222

223+
/**
224+
* @depends testHasPaths
225+
*/
226+
public function testRouteScopes($paths)
227+
{
228+
$this->assertEquals(['user-read'], $paths['/users']['get']['security'][Generator::SECURITY_DEFINITION_NAME]);
229+
$this->assertEquals(['user-write', 'user-read'], $paths['/users']['post']['security'][Generator::SECURITY_DEFINITION_NAME]);
230+
}
231+
223232
public function testOverwriteIgnoreMethods()
224233
{
225234
$docs = $this->getDocsWithNewConfig(['ignoredMethods' => []]);

tests/TestCase.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ protected function getPackageProviders($app)
1414

1515
protected function getEnvironmentSetUp($app)
1616
{
17-
$app['router']->get('/users', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@index');
18-
$app['router']->get('/users/{id}', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@show');
19-
$app['router']->post('/users', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@store');
20-
$app['router']->get('/users/details', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@details');
21-
$app['router']->get('/users/ping', function () {
22-
return 'pong';
17+
$app['router']->middleware(['some-middleware', 'scope:user-read'])->group(function() use($app) {
18+
$app['router']->get('/users', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@index');
19+
$app['router']->get('/users/{id}', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@show');
20+
$app['router']->post('/users', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@store')
21+
->middleware('scopes:user-write,user-read');
22+
$app['router']->get('/users/details', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\UserController@details');
23+
$app['router']->get('/users/ping', function () {
24+
return 'pong';
25+
});
2326
});
27+
2428
$app['router']->get('/api', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\ApiController@index');
2529
$app['router']->put('/api/store', 'Mtrajano\\LaravelSwagger\\Tests\\Stubs\\Controllers\\ApiController@store');
2630

0 commit comments

Comments
 (0)