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
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import static java.lang.annotation.ElementType.METHOD;

/**
* The annotation may be applied at class or method level, or in {@link io.swagger.v3.oas.annotations.Operation#security()} ()} to define security requirements for the
* The annotation may be applied at class or method level, or in {@link io.swagger.v3.oas.annotations.Operation#security()} to define security requirements for the
* single operation (when applied at method level) or for all operations of a class (when applied at class level).
* <p>It can also be used in {@link io.swagger.v3.oas.annotations.OpenAPIDefinition#security()} to define spec level security.</p>
* <p>{@link SecurityRequirement#combine()} can be used to define multiple security requirements at the same time, requiring each one of them.
* If only one of multiple security schemes is required, use multiple {@link SecurityRequirement} annotations.</p>
*
* @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.4/versions/3.0.4.md#security-requirement-object">Security Requirement (OpenAPI specification)</a>
* @see io.swagger.v3.oas.annotations.OpenAPIDefinition
Expand All @@ -26,10 +28,11 @@
public @interface SecurityRequirement {
/**
* This name must correspond to a declared SecurityRequirement.
* <p>Exactly one of this and {@link #combine()} must be set.</p>
*
* @return String name
*/
String name();
String name() default "";

/**
* If the security scheme is of type "oauth2" or "openIdConnect", then the value is a list of scope names required for the execution.
Expand All @@ -38,4 +41,13 @@
* @return String array of scopes
*/
String[] scopes() default {};

/**
* If multiple requirements apply at the same time, use this value instead of {@link #name()} and {@link #scopes()}.
* If any one of multiple security schemes is required, use multiple {@link SecurityRequirement} annotations instead.
* <p>Exactly one of this and {@link #name()} must be set.</p>
*
* @return SecurityRequirementEntry array of requirements
*/
SecurityRequirementEntry[] combine() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.swagger.v3.oas.annotations.security;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* The annotation may be applied in {@link SecurityRequirement#combine()} to define combined security requirements for the
* single operation.
*
* @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.4/versions/3.0.4.md#security-requirement-object">Security Requirement (OpenAPI specification)</a>
* @see io.swagger.v3.oas.annotations.security.SecurityRequirement
**/
@Target({ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityRequirementEntry {
/**
* This name must correspond to a declared SecurityRequirement.
*
* @return String name
*/
String name();

/**
* If the security scheme is of type "oauth2" or "openIdConnect", then the value is a list of scope names required for the execution.
* For other security scheme types, the array must be empty.
*
* @return String array of scopes
*/
String[] scopes() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,28 @@ public static Optional<List<SecurityRequirement>> getSecurityRequirements(io.swa
}
List<SecurityRequirement> securityRequirements = new ArrayList<>();
for (io.swagger.v3.oas.annotations.security.SecurityRequirement securityRequirementApi : securityRequirementsApi) {
if (StringUtils.isBlank(securityRequirementApi.name())) {
continue;
}
SecurityRequirement securityRequirement = new SecurityRequirement();
if (securityRequirementApi.scopes().length > 0) {
securityRequirement.addList(securityRequirementApi.name(), Arrays.asList(securityRequirementApi.scopes()));
} else {
securityRequirement.addList(securityRequirementApi.name());
if (securityRequirementApi.combine().length > 0) {
for (io.swagger.v3.oas.annotations.security.SecurityRequirementEntry entry : securityRequirementApi.combine()) {
if (StringUtils.isBlank(entry.name())) {
continue;
}
if (entry.scopes().length > 0) {
securityRequirement.addList(entry.name(), Arrays.asList(entry.scopes()));
} else {
securityRequirement.addList(entry.name());
}
}
}
else {
if (StringUtils.isBlank(securityRequirementApi.name())) {
continue;
}
if (securityRequirementApi.scopes().length > 0) {
securityRequirement.addList(securityRequirementApi.name(), Arrays.asList(securityRequirementApi.scopes()));
} else {
securityRequirement.addList(securityRequirementApi.name());
}
}
securityRequirements.add(securityRequirement);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.swagger.v3.oas.annotations.security.OAuthFlows;
import io.swagger.v3.oas.annotations.security.OAuthScope;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityRequirementEntry;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import org.testng.annotations.Test;

Expand All @@ -19,7 +20,7 @@

public class SecurityTest extends AbstractAnnotationTest {
@Test
public void testSecuritySheme() {
public void testSecurityScheme() {
String openApiYAML = readIntoYaml(SecurityTest.OAuth2SchemeOnClass.class);
int start = openApiYAML.indexOf("components:");
String extractedYAML = openApiYAML.substring(start, openApiYAML.length() - 1);
Expand Down Expand Up @@ -90,7 +91,7 @@ public void testSecurityRequirement() throws IOException {
}

@Test
public void testMultipleSecurityShemes() {
public void testMultipleSecuritySchemes() {
String openApiYAML = readIntoYaml(SecurityTest.MultipleSchemesOnClass.class);
int start = openApiYAML.indexOf("components:");
String extractedYAML = openApiYAML.substring(start, openApiYAML.length() - 1);
Expand All @@ -112,6 +113,30 @@ public void testMultipleSecurityShemes() {

}

@Test
public void testCombinedSecurityRequirements() {
String openApiYAML = readIntoYaml(SecurityTest.CombinedSecurityRequirementsOnClass.class);
int start = openApiYAML.indexOf("security:");
int end = openApiYAML.indexOf("components:");
String extractedYAML = openApiYAML.substring(start, end);
String expectedYAML = "security:\n" +
"- api_key: []\n" +
" myOauth2Security: []\n";
assertEquals(extractedYAML, expectedYAML);
}

@Test
public void testSecurityRequirementAlternatives() {
String openApiYAML = readIntoYaml(SecurityRequirementAlternativesOnClass.class);
int start = openApiYAML.indexOf("security:");
int end = openApiYAML.indexOf("components:");
String extractedYAML = openApiYAML.substring(start, end);
String expectedYAML = "security:\n" +
"- api_key: []\n" +
"- myOauth2Security: []\n";
assertEquals(extractedYAML, expectedYAML);
}

@Test
public void testTicket2767() {
String openApiYAML = readIntoYaml(SecurityTest.Ticket2767.class);
Expand Down Expand Up @@ -169,6 +194,34 @@ static class MultipleSchemesOnClass {

}

@OpenAPIDefinition(
security = {@SecurityRequirement(combine = { @SecurityRequirementEntry(name = "api_key"), @SecurityRequirementEntry(name = "myOauth2Security") })}
)
@SecurityScheme(name = "api_key", type = SecuritySchemeType.APIKEY, paramName = "API_KEY")
@SecurityScheme(name = "myOauth2Security",
type = SecuritySchemeType.OAUTH2,
in = SecuritySchemeIn.HEADER,
flows = @OAuthFlows(
implicit = @OAuthFlow(authorizationUrl = "http://url.com/auth",
scopes = @OAuthScope(name = "write:pets", description = "modify pets in your account"))))
static class CombinedSecurityRequirementsOnClass {

}

@OpenAPIDefinition(
security = {@SecurityRequirement(name = "api_key"), @SecurityRequirement(name = "myOauth2Security")}
)
@SecurityScheme(name = "api_key", type = SecuritySchemeType.APIKEY, paramName = "API_KEY")
@SecurityScheme(name = "myOauth2Security",
type = SecuritySchemeType.OAUTH2,
in = SecuritySchemeIn.HEADER,
flows = @OAuthFlows(
implicit = @OAuthFlow(authorizationUrl = "http://url.com/auth",
scopes = @OAuthScope(name = "write:pets", description = "modify pets in your account"))))
static class SecurityRequirementAlternativesOnClass {

}


@OpenAPIDefinition(
security = {@SecurityRequirement(name = "basicAuth")},
Expand Down