Skip to content

disable global security for particular operation #2844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jmilkiewicz opened this issue Jun 18, 2018 · 6 comments
Open

disable global security for particular operation #2844

jmilkiewicz opened this issue Jun 18, 2018 · 6 comments

Comments

@jmilkiewicz
Copy link

I am using swagger.core.v3 in version 2.0.2 to generate openAPI 3.0 definition files and
I am having trouble to disable "security" for a particular endpoint.
I have global securitySchemes and root security element defined:

 Info info = new Info()
            .title("someTitle")
            .description("some description")
            .version("1.0")

    SecurityScheme jwtSecurity = new SecurityScheme()
            .type(SecurityScheme.Type.HTTP)
            .name("Authorization")
            .in(SecurityScheme.In.HEADER)
            .scheme("bearer")
            .bearerFormat("JWT");

    String securitySchemaName = "JWT";
    OpenAPI oas = new OpenAPI()
            .info(info)
            .components(new Components().addSecuritySchemes(securitySchemaName, jwtSecurity))
            .addSecurityItem(new SecurityRequirement().addList(securitySchemaName));

    SwaggerConfiguration oasConfig = new SwaggerConfiguration()
            .openAPI(oas)
            .prettyPrint(true)
            .resourcePackages(Stream.of("my.resources.package")
                    .collect(Collectors.toSet()));
    environment.jersey().register(new OpenApiResource()
            .openApiConfiguration(oasConfig));

And definition file is nicely generated:

{
  "openapi" : "3.0.1",
  "security" : [ {
    "JWT" : [ ]
  } ],
  "paths" : {   
    ...
  },
  "components" : {
    "schemas" : {
     ...
    },
    "securitySchemes" : {
      "JWT" : {
        "type" : "http",
        "scheme" : "bearer",
        "bearerFormat" : "JWT"
      }
    }
  }
}

According to OPEN API 3 spec https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#securityRequirementObject i shall be able to override global "security requirement" for an individual operation. I would like to "disable" JWT security for a few operations and according to https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#securityRequirementObject it can be done by

To remove a top-level security declaration, an empty array can be used.

I simply wanna specify "NO Security" for a particular opetration:

    @POST
@Operation(
        summary = "authenticate user",
        responses = {
                @ApiResponse(responseCode = "200", description = "when user is successfully authenticated",
                        content = @Content(schema = @Schema(implementation = AuthenticateUserOutput.class))),                   
                @ApiResponse(responseCode = "401", description = "when email/password not valid or user is blocked/inactive"),
        }
        ,security = what to put here ?
)

I tried
security = {}
or
security = @SecurityRequirement(name ="")
but in both cases no security element within operation is generated at all....

@frantuma
Copy link
Member

at the moment you can achieve what you need using filter, as in the example below; possibly related annotation processing will be enhanced to allow for annotation only handling of such a case, but no ETA atm

    class SecurityFilter extends AbstractSpecFilter {
        @Override
        public Optional<Operation> filterOperation(Operation operation, ApiDescription api, Map<String, List<String>> params, Map<String, String> cookies, Map<String, List<String>> headers) {
            if ("security".equals(operation.getOperationId())) { // or whatever operationId..
                operation.setSecurity(new ArrayList<>());
                return Optional.of(operation);
            }
            return super.filterOperation(operation, api, params, cookies, headers);
        }
    }

in your case (usage of provided OpenApiResource) you can specify filter providing fully qualified name in config object:

        SwaggerConfiguration oasConfig = new SwaggerConfiguration()
                .openAPI(oas)
                .prettyPrint(true)
                .filterClass("fully.qualified.SecurityFilter")
                .resourcePackages(Stream.of("my.resources.package")
                        .collect(Collectors.toSet()));

@mafor
Copy link

mafor commented Dec 20, 2019

Interestingly, it works on the SwaggerHub: security-override-test

@ratijas
Copy link

ratijas commented Feb 17, 2020

bump

@sbarfurth
Copy link

I found that simply adding io.swagger.v3.oas.annotations.security.SecurityRequirements as an annotation will do what you want.

@RestController
class Controller {

    @SecurityRequirements // This is it
    @GetMapping("/api/v1/endpoint")
    public String endpoint() {
        return "no security required!";
    }

}

At least for me this disabled all security requirements for the method.

@Frantch
Copy link

Frantch commented Mar 16, 2022

Doesn't work for me with Swagger 2.1.13

@doubleaxe
Copy link

doubleaxe commented May 6, 2022

To make @sbarfurth solution to work on latest Swagger, 2.1.13, you should also hack inside reader class. You also may place custom processing there, for example my security classes are annotated with @Secured annotation, and I just remove global security info for classes without this annotation.

readerClass: my.SwaggerReader
prettyPrint: true
openAPI:
  info:
    title: MY REST API

or

swaggerConfiguration.readerClass("my.SwaggerReader");

then

public class SwaggerReader extends Reader {

	@Override
	protected Operation parseMethod(Class<?> cls,
			Method method,
			List<Parameter> globalParameters,
			Produces methodProduces,
			Produces classProduces,
			Consumes methodConsumes,
			Consumes classConsumes,
			List<SecurityRequirement> classSecurityRequirements,
			Optional<ExternalDocumentation> classExternalDocs,
			Set<String> classTags,
			List<Server> classServers,
			boolean isSubresource,
			RequestBody parentRequestBody,
			ApiResponses parentResponses,
			JsonView jsonViewAnnotation,
			ApiResponse[] classResponses,
			AnnotatedMethod annotatedMethod
			) {
		Operation parsedOperation = super.parseMethod(cls,
				method,
				globalParameters,
				methodProduces,
				classProduces,
				methodConsumes,
				classConsumes,
				classSecurityRequirements,
				classExternalDocs,
				classTags,
				classServers,
				isSubresource,
				parentRequestBody,
				parentResponses,
				jsonViewAnnotation,
				classResponses,
				annotatedMethod
				);
		SecurityRequirements security[] = method.getAnnotationsByType(SecurityRequirements.class);
		if((security != null) && (security.length == 1) &&
				((security[0].value() == null) || (security[0].value().length == 0))) {
        	  	parsedOperation.setSecurity(new ArrayList<>(0));
		}
        
		return parsedOperation;
	}
}

and now this should work

@RestController
class Controller {

    @SecurityRequirements // This is it
    @GetMapping("/api/v1/endpoint")
    public String endpoint() {
        return "no security required!";
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants