1
18
19 package org.springdoc.core;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
28
29 import io.swagger.v3.core.util.AnnotationsUtils;
30 import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
31 import io.swagger.v3.oas.annotations.security.OAuthScope;
32 import io.swagger.v3.oas.models.Operation;
33 import io.swagger.v3.oas.models.security.OAuthFlow;
34 import io.swagger.v3.oas.models.security.OAuthFlows;
35 import io.swagger.v3.oas.models.security.Scopes;
36 import io.swagger.v3.oas.models.security.SecurityRequirement;
37 import io.swagger.v3.oas.models.security.SecurityScheme;
38 import org.apache.commons.lang3.StringUtils;
39
40 import org.springframework.core.annotation.AnnotatedElementUtils;
41 import org.springframework.util.CollectionUtils;
42 import org.springframework.web.method.HandlerMethod;
43
44 class SecurityParser {
45
46 private final PropertyResolverUtils propertyResolverUtils;
47
48 public SecurityParser(PropertyResolverUtils propertyResolverUtils) {
49 this.propertyResolverUtils = propertyResolverUtils;
50 }
51
52 private static boolean isEmpty(io.swagger.v3.oas.annotations.security.OAuthFlows oAuthFlows) {
53 boolean result;
54 if (oAuthFlows == null)
55 result = true;
56 else if (!isEmpty(oAuthFlows.implicit()) || !isEmpty(oAuthFlows.authorizationCode()) || !isEmpty(oAuthFlows.clientCredentials()) || !isEmpty(oAuthFlows.password()))
57 result = false;
58 else result = oAuthFlows.extensions().length <= 0;
59 return result;
60 }
61
62 private static boolean isEmpty(io.swagger.v3.oas.annotations.security.OAuthFlow oAuthFlow) {
63 boolean result;
64 if (oAuthFlow == null)
65 result = true;
66 else if (!StringUtils.isBlank(oAuthFlow.authorizationUrl()) || !StringUtils.isBlank(oAuthFlow.refreshUrl()) || !StringUtils.isBlank(oAuthFlow.tokenUrl()) || !isEmpty(oAuthFlow.scopes()))
67 result = false;
68 else result = oAuthFlow.extensions().length <= 0;
69 return result;
70 }
71
72 private static boolean isEmpty(OAuthScope[] scopes) {
73 boolean result = false;
74 if (scopes == null || scopes.length == 0)
75 result = true;
76 return result;
77 }
78
79 public io.swagger.v3.oas.annotations.security.SecurityRequirement[] getSecurityRequirements(
80 HandlerMethod method) {
81
82 io.swagger.v3.oas.annotations.security.SecurityRequirements classSecurity = AnnotatedElementUtils.findMergedAnnotation(method.getBeanType(), io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
83
84 io.swagger.v3.oas.annotations.security.SecurityRequirements methodSecurity = AnnotatedElementUtils.findMergedAnnotation(method.getMethod(), io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
85
86 Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> allSecurityTags = null;
87
88 if (classSecurity != null)
89 allSecurityTags = new HashSet<>(Arrays.asList(classSecurity.value()));
90 if (methodSecurity != null)
91 allSecurityTags = addSecurityRequirements(allSecurityTags, new HashSet<>(Arrays.asList(methodSecurity.value())));
92
93 if (CollectionUtils.isEmpty(allSecurityTags)) {
94
95 Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> securityRequirementsClassList = AnnotatedElementUtils.findMergedRepeatableAnnotations(
96 method.getBeanType(),
97 io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
98
99 Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> securityRequirementsMethodList = AnnotatedElementUtils.findMergedRepeatableAnnotations(method.getMethod(),
100 io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
101 if (!CollectionUtils.isEmpty(securityRequirementsClassList))
102 allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsClassList);
103 if (!CollectionUtils.isEmpty(securityRequirementsMethodList))
104 allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsMethodList);
105 }
106
107 return (allSecurityTags != null) ? allSecurityTags.toArray(new io.swagger.v3.oas.annotations.security.SecurityRequirement[0]) : null;
108 }
109
110 private Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> addSecurityRequirements(Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> allSecurityTags, Set<io.swagger.v3.oas.annotations.security.SecurityRequirement> securityRequirementsClassList) {
111 if (allSecurityTags == null)
112 allSecurityTags = new HashSet<>();
113 allSecurityTags.addAll(securityRequirementsClassList);
114 return allSecurityTags;
115 }
116
117 public Optional<List<SecurityRequirement>> getSecurityRequirements(
118 io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirementsApi) {
119 if (securityRequirementsApi == null || securityRequirementsApi.length == 0)
120 return Optional.empty();
121 List<SecurityRequirement> securityRequirements = new ArrayList<>();
122 for (io.swagger.v3.oas.annotations.security.SecurityRequirement securityRequirementApi : securityRequirementsApi) {
123 if (StringUtils.isBlank(securityRequirementApi.name()))
124 continue;
125 SecurityRequirement securityRequirement = new SecurityRequirement();
126 if (securityRequirementApi.scopes().length > 0)
127 securityRequirement.addList(securityRequirementApi.name(), Arrays.asList(securityRequirementApi.scopes()));
128 else
129 securityRequirement.addList(securityRequirementApi.name());
130 securityRequirements.add(securityRequirement);
131 }
132 if (securityRequirements.isEmpty())
133 return Optional.empty();
134 return Optional.of(securityRequirements);
135 }
136
137 public Optional<SecuritySchemePair> getSecurityScheme(
138 io.swagger.v3.oas.annotations.security.SecurityScheme securityScheme) {
139 if (securityScheme == null)
140 return Optional.empty();
141 String key = null;
142 SecurityScheme securitySchemeObject = new SecurityScheme();
143
144 if (StringUtils.isNotBlank(securityScheme.in().toString()))
145 securitySchemeObject.setIn(getIn(securityScheme.in().toString()));
146
147 if (StringUtils.isNotBlank(securityScheme.type().toString()))
148 securitySchemeObject.setType(getType(securityScheme.type().toString()));
149
150 if (StringUtils.isNotBlank(securityScheme.openIdConnectUrl()))
151 securitySchemeObject.setOpenIdConnectUrl(propertyResolverUtils.resolve(securityScheme.openIdConnectUrl()));
152
153 if (StringUtils.isNotBlank(securityScheme.scheme()))
154 securitySchemeObject.setScheme(securityScheme.scheme());
155
156 if (StringUtils.isNotBlank(securityScheme.bearerFormat()))
157 securitySchemeObject.setBearerFormat(securityScheme.bearerFormat());
158
159 if (StringUtils.isNotBlank(securityScheme.description()))
160 securitySchemeObject.setDescription(securityScheme.description());
161
162 if (StringUtils.isNotBlank(securityScheme.ref()))
163 securitySchemeObject.set$ref(securityScheme.ref());
164
165 if (StringUtils.isNotBlank(securityScheme.name())) {
166 key = securityScheme.name();
167 if (SecuritySchemeType.APIKEY.toString().equals(securitySchemeObject.getType().toString()))
168 securitySchemeObject.setName(securityScheme.name());
169 }
170 if (StringUtils.isNotBlank(securityScheme.paramName()))
171 securitySchemeObject.setName(securityScheme.paramName());
172
173 if (securityScheme.extensions().length > 0) {
174 Map<String, Object> extensions = AnnotationsUtils.getExtensions(securityScheme.extensions());
175 extensions.forEach(securitySchemeObject::addExtension);
176 }
177
178 getOAuthFlows(securityScheme.flows()).ifPresent(securitySchemeObject::setFlows);
179
180 SecuritySchemePair result = new SecuritySchemePair(key, securitySchemeObject);
181 return Optional.of(result);
182 }
183
184 public void buildSecurityRequirement(
185 io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements, Operation operation) {
186 Optional<List<SecurityRequirement>> requirementsObject = this.getSecurityRequirements(securityRequirements);
187 requirementsObject.ifPresent(requirements -> requirements.stream()
188 .filter(r -> operation.getSecurity() == null || !operation.getSecurity().contains(r))
189 .forEach(operation::addSecurityItem));
190 }
191
192 private Optional<OAuthFlows> getOAuthFlows(io.swagger.v3.oas.annotations.security.OAuthFlows oAuthFlows) {
193 if (isEmpty(oAuthFlows))
194 return Optional.empty();
195
196 OAuthFlows oAuthFlowsObject = new OAuthFlows();
197 if (oAuthFlows.extensions().length > 0) {
198 Map<String, Object> extensions = AnnotationsUtils.getExtensions(oAuthFlows.extensions());
199 extensions.forEach(oAuthFlowsObject::addExtension);
200 }
201 getOAuthFlow(oAuthFlows.authorizationCode()).ifPresent(oAuthFlowsObject::setAuthorizationCode);
202 getOAuthFlow(oAuthFlows.clientCredentials()).ifPresent(oAuthFlowsObject::setClientCredentials);
203 getOAuthFlow(oAuthFlows.implicit()).ifPresent(oAuthFlowsObject::setImplicit);
204 getOAuthFlow(oAuthFlows.password()).ifPresent(oAuthFlowsObject::setPassword);
205 return Optional.of(oAuthFlowsObject);
206 }
207
208 private Optional<OAuthFlow> getOAuthFlow(io.swagger.v3.oas.annotations.security.OAuthFlow oAuthFlow) {
209 if (isEmpty(oAuthFlow)) {
210 return Optional.empty();
211 }
212 OAuthFlow oAuthFlowObject = new OAuthFlow();
213 if (StringUtils.isNotBlank(oAuthFlow.authorizationUrl()))
214 oAuthFlowObject.setAuthorizationUrl(propertyResolverUtils.resolve(oAuthFlow.authorizationUrl()));
215
216 if (StringUtils.isNotBlank(oAuthFlow.refreshUrl()))
217 oAuthFlowObject.setRefreshUrl(propertyResolverUtils.resolve(oAuthFlow.refreshUrl()));
218
219 if (StringUtils.isNotBlank(oAuthFlow.tokenUrl()))
220 oAuthFlowObject.setTokenUrl(propertyResolverUtils.resolve(oAuthFlow.tokenUrl()));
221
222 if (oAuthFlow.extensions().length > 0) {
223 Map<String, Object> extensions = AnnotationsUtils.getExtensions(oAuthFlow.extensions());
224 extensions.forEach(oAuthFlowObject::addExtension);
225 }
226 getScopes(oAuthFlow.scopes()).ifPresent(oAuthFlowObject::setScopes);
227 return Optional.of(oAuthFlowObject);
228 }
229
230 private Optional<Scopes> getScopes(OAuthScope[] scopes) {
231 if (isEmpty(scopes))
232 return Optional.empty();
233
234 Scopes scopesObject = new Scopes();
235 Arrays.stream(scopes).forEach(scope -> scopesObject.addString(scope.name(), scope.description()));
236 return Optional.of(scopesObject);
237 }
238
239 private SecurityScheme.In getIn(String value) {
240 return Arrays.stream(SecurityScheme.In.values()).filter(i -> i.toString().equals(value)).findFirst()
241 .orElse(null);
242 }
243
244 private SecurityScheme.Type getType(String value) {
245 return Arrays.stream(SecurityScheme.Type.values()).filter(i -> i.toString().equals(value)).findFirst()
246 .orElse(null);
247 }
248
249 }