@ApiOperation should inherit authorizations from @Api but doesn't
See original GitHub issueIf I have a Controller like this
@RestController
@Api(authorizations = { @Authorization(value = "oauth")})
public class AResource {
@GetMapping("/foo")
@ApiOperation
public SomeFoo foo() {
return ...;
}
@GetMapping("/bar")
@ApiOperation(authorizations = {@Authorization(value = "something") })
public SomeBar bar() {
return ....;
}
}
one would expect the resource /foo
to require oauth
authorization while /bar
requires some other form (something
). However, it seems that Sprinfox never looks at Api.authorizations
.
I suggest springfox should be extended with a new OperationBuilderPlugin
that evaluates Api.authorizations
. It could look much like the one below, which works in my test setup.
/*
*
* Copyright 2015-2016 the original author or authors.
*
* Licensed 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.
*
*
*/
package com.example.demo;
import static com.google.common.base.Strings.*;
import static com.google.common.collect.Lists.*;
import static com.google.common.collect.Maps.*;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Authorization;
import io.swagger.annotations.AuthorizationScope;
import springfox.documentation.builders.AuthorizationScopeBuilder;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
public class ControllerAuthReader implements OperationBuilderPlugin {
private static final Logger LOG = LoggerFactory.getLogger(ControllerAuthReader.class);
@Override
public void apply(final OperationContext context) {
List<SecurityContext> securityContexts = context.securityContext();
Map<String, SecurityReference> securityReferences = newHashMap();
for (SecurityContext each : securityContexts) {
securityReferences.putAll(
FluentIterable.from(each.securityForOperation(context))
.uniqueIndex(byReferenceName()));
}
Optional<Api> controllerAnnotation = context.findControllerAnnotation(Api.class);
Optional<ApiOperation> apiOperationAnnotation = context.findAnnotation(ApiOperation.class);
if (controllerAnnotation.isPresent()) {
// ApiOperation.authorizations takes precedence over Api.authorizations
if (!authorizationReferences(controllerAnnotation.get()).isEmpty()
&& (!apiOperationAnnotation.isPresent() || authorizationReferences(apiOperationAnnotation.get()).isEmpty())) {
List<SecurityReference> securityReferenceOverrides = newArrayList();
for (Authorization authorization : authorizationReferences(controllerAnnotation.get())) {
// The rest is copied verbatim from OperationAuthReader - it should rather be made reusable
String value = authorization.value();
AuthorizationScope[] scopes = authorization.scopes();
List<springfox.documentation.service.AuthorizationScope> authorizationScopeList = newArrayList();
for (AuthorizationScope authorizationScope : scopes) {
String description = authorizationScope.description();
String scope = authorizationScope.scope();
// @Authorization has a default blank authorization scope, which we need to
// ignore in the case of api keys.
if (!isNullOrEmpty(scope)) {
authorizationScopeList.add(
new AuthorizationScopeBuilder()
.scope(scope)
.description(description)
.build());
}
}
springfox.documentation.service.AuthorizationScope[] authorizationScopes
= authorizationScopeList
.toArray(new springfox.documentation.service.AuthorizationScope[0]);
SecurityReference securityReference =
SecurityReference.builder()
.reference(value)
.scopes(authorizationScopes)
.build();
securityReferenceOverrides.add(securityReference);
}
securityReferences.putAll(FluentIterable.from(securityReferenceOverrides)
.uniqueIndex(byReferenceName()));
}
}
LOG.debug("Authorization count {} for method {}", securityReferences.size(), context.getName());
context.operationBuilder().authorizations(securityReferences.values());
}
private Function<SecurityReference, String> byReferenceName() {
return new Function<SecurityReference, String>() {
@Override
public String apply(final SecurityReference input) {
return input.getReference();
}
};
}
private FluentIterable<Authorization> authorizationReferences(final Api apiAnnotation) {
return FluentIterable.from(apiAnnotation.authorizations())
.filter(nonEmptyAuthorization());
}
private FluentIterable<Authorization> authorizationReferences(final ApiOperation apiOperationAnnotation) {
return FluentIterable.from(apiOperationAnnotation.authorizations())
.filter(nonEmptyAuthorization());
}
private Predicate<Authorization> nonEmptyAuthorization() {
return new Predicate<Authorization>() {
@Override
public boolean apply(final Authorization input) {
return !Strings.isNullOrEmpty(input.value());
}
};
}
@Override
public boolean supports(final DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:5 (2 by maintainers)
Top Results From Across the Web
Swagger UI does not display response models from custom ...
1 Answer 1 · I am using springfox-swagger2 v3. · And this is not the answer I was looking for. · @Dipu I...
Read more >Carbon Black Cloud: What are the differences between API ...
This type of permissions rule is inherited by child processes, and should be very limited in use. Performs any API operation - the...
Read more >Make Swagger UI usable even with class inheritance ... - Wei He
In this article, I would like to share my “code first” experience with Swagger during implementing a REST API which data models have...
Read more >OpenAPI Specification - Version 2.0 - Swagger
Version 2.0 specification defines a set of files required to describe an API. These files can then be used by the Swagger-UI project...
Read more >Swagger Annotations for Rest API Documentation - Java Guides
Only classes that are annotated with @Api will be scanned by Swagger. ... @ApiOperation(value = "Add a new pet to the store", authorizations...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Any progress on this? I am facing the same issue. Defining authorization scopes for each ApiOperation is tedious and we would like to do that at Api resource level.
This is changed after the support for open api went in. Open to accepting PRs for this but this is deprioritized since we will now be using @OpenAPIDefinition