All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springdoc.core.SecurityService Maven / Gradle / Ivy

The newest version!
/*
 *
 *  *
 *  *  *
 *  *  *  *
 *  *  *  *  * Copyright 2019-2022 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
 *  *  *  *  *
 *  *  *  *  *      https://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 org.springdoc.core;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.OAuthScope;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.Scopes;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.apache.commons.lang3.StringUtils;

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;

/**
 * The type Security parser.
 * @author bnasslahsen
 */
public class SecurityService {

	/**
	 * The Property resolver utils.
	 */
	private final PropertyResolverUtils propertyResolverUtils;

	/**
	 * Instantiates a new Security parser.
	 *
	 * @param propertyResolverUtils the property resolver utils
	 */
	public SecurityService(PropertyResolverUtils propertyResolverUtils) {
		this.propertyResolverUtils = propertyResolverUtils;
	}

	/**
	 * Is empty boolean.
	 *
	 * @param oAuthFlows the o auth flows
	 * @return the boolean
	 */
	private static boolean isEmpty(io.swagger.v3.oas.annotations.security.OAuthFlows oAuthFlows) {
		boolean result;
		if (oAuthFlows == null)
			result = true;
		else if (!isEmpty(oAuthFlows.implicit()) || !isEmpty(oAuthFlows.authorizationCode()) || !isEmpty(oAuthFlows.clientCredentials()) || !isEmpty(oAuthFlows.password()))
			result = false;
		else result = oAuthFlows.extensions().length <= 0;
		return result;
	}

	/**
	 * Is empty boolean.
	 *
	 * @param oAuthFlow the o auth flow
	 * @return the boolean
	 */
	private static boolean isEmpty(io.swagger.v3.oas.annotations.security.OAuthFlow oAuthFlow) {
		boolean result;
		if (oAuthFlow == null)
			result = true;
		else if (!StringUtils.isBlank(oAuthFlow.authorizationUrl()) || !StringUtils.isBlank(oAuthFlow.refreshUrl()) || !StringUtils.isBlank(oAuthFlow.tokenUrl()) || !isEmpty(oAuthFlow.scopes()))
			result = false;
		else result = oAuthFlow.extensions().length <= 0;
		return result;
	}

	/**
	 * Is empty boolean.
	 *
	 * @param scopes the scopes
	 * @return the boolean
	 */
	private static boolean isEmpty(OAuthScope[] scopes) {
		return scopes == null || scopes.length == 0;
	}

	/**
	 * Get security requirements io . swagger . v 3 . oas . annotations . security . security requirement [ ].
	 *
	 * @param handlerMethod the handlerMethod
	 * @return the io . swagger . v 3 . oas . annotations . security . security requirement [ ]
	 */
	public io.swagger.v3.oas.annotations.security.SecurityRequirement[] getSecurityRequirements(
			HandlerMethod handlerMethod) {
		// class SecurityRequirements
		Class beanType = handlerMethod.getBeanType();
		Set allSecurityTags = getSecurityRequirementsForClass(beanType);

		// handlerMethod SecurityRequirements
		Method method = handlerMethod.getMethod();
		allSecurityTags = getSecurityRequirementsForMethod(method, allSecurityTags);

		return (allSecurityTags != null) ? allSecurityTags.toArray(new io.swagger.v3.oas.annotations.security.SecurityRequirement[0]) : null;
	}

	/**
	 * Gets security requirements for method.
	 *
	 * @param method the method
	 * @param allSecurityTags the all security tags
	 * @return the security requirements for method
	 */
	public Set getSecurityRequirementsForMethod(Method method, Set allSecurityTags) {
		io.swagger.v3.oas.annotations.security.SecurityRequirements methodSecurity = AnnotatedElementUtils.findMergedAnnotation(method, io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
		if (methodSecurity != null)
			allSecurityTags = addSecurityRequirements(allSecurityTags, new HashSet<>(Arrays.asList(methodSecurity.value())));
		if (CollectionUtils.isEmpty(allSecurityTags)) {
			// handlerMethod SecurityRequirement
			Set securityRequirementsMethodList = AnnotatedElementUtils.findMergedRepeatableAnnotations(method,
					io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
			if (!CollectionUtils.isEmpty(securityRequirementsMethodList))
				allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsMethodList);
		}
		return allSecurityTags;
	}

	/**
	 * Gets security requirements for class.
	 *
	 * @param beanType the bean type
	 * @return the security requirements for class
	 */
	public Set getSecurityRequirementsForClass(Class beanType) {
		Set allSecurityTags = null;
		io.swagger.v3.oas.annotations.security.SecurityRequirements classSecurity = AnnotatedElementUtils.findMergedAnnotation(beanType, io.swagger.v3.oas.annotations.security.SecurityRequirements.class);
		if (classSecurity != null)
			allSecurityTags = new HashSet<>(Arrays.asList(classSecurity.value()));
		if (CollectionUtils.isEmpty(allSecurityTags)) {
			// class SecurityRequirement
			Set securityRequirementsClassList = AnnotatedElementUtils.findMergedRepeatableAnnotations(
					beanType,
					io.swagger.v3.oas.annotations.security.SecurityRequirement.class);
			if (!CollectionUtils.isEmpty(securityRequirementsClassList))
				allSecurityTags = addSecurityRequirements(allSecurityTags, securityRequirementsClassList);
		}
		return allSecurityTags;
	}

	/**
	 * Add security requirements set.
	 *
	 * @param allSecurityTags the all security tags
	 * @param securityRequirementsClassList the security requirements class list
	 * @return the set
	 */
	private Set addSecurityRequirements(Set allSecurityTags, Set securityRequirementsClassList) {
		if (allSecurityTags == null)
			allSecurityTags = new HashSet<>();
		allSecurityTags.addAll(securityRequirementsClassList);
		return allSecurityTags;
	}

	/**
	 * Gets security requirements.
	 *
	 * @param securityRequirementsApi the security requirements api
	 * @return the security requirements
	 */
	public Optional> getSecurityRequirements(
			io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirementsApi) {
		if (securityRequirementsApi == null || securityRequirementsApi.length == 0)
			return Optional.empty();
		List 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());
			securityRequirements.add(securityRequirement);
		}
		if (securityRequirements.isEmpty())
			return Optional.empty();
		return Optional.of(securityRequirements);
	}

	/**
	 * Gets security scheme.
	 *
	 * @param securityScheme the security scheme
	 * @param locale the locale
	 * @return the security scheme
	 */
	 Optional getSecurityScheme(
			io.swagger.v3.oas.annotations.security.SecurityScheme securityScheme, Locale locale) {
		if (securityScheme == null)
			return Optional.empty();
		String key = null;
		SecurityScheme securitySchemeObject = new SecurityScheme();

		if (StringUtils.isNotBlank(securityScheme.in().toString()))
			securitySchemeObject.setIn(getIn(securityScheme.in().toString()));

		if (StringUtils.isNotBlank(securityScheme.type().toString()))
			securitySchemeObject.setType(getType(securityScheme.type().toString()));

		if (StringUtils.isNotBlank(securityScheme.openIdConnectUrl()))
			securitySchemeObject.setOpenIdConnectUrl(propertyResolverUtils.resolve(securityScheme.openIdConnectUrl(), locale));

		if (StringUtils.isNotBlank(securityScheme.scheme()))
			securitySchemeObject.setScheme(securityScheme.scheme());

		if (StringUtils.isNotBlank(securityScheme.bearerFormat()))
			securitySchemeObject.setBearerFormat(securityScheme.bearerFormat());

		if (StringUtils.isNotBlank(securityScheme.description()))
			securitySchemeObject.setDescription(securityScheme.description());

		if (StringUtils.isNotBlank(securityScheme.ref()))
			securitySchemeObject.set$ref(securityScheme.ref());

		if (StringUtils.isNotBlank(securityScheme.name())) {
			key = securityScheme.name();
			if (SecuritySchemeType.APIKEY.toString().equals(securitySchemeObject.getType().toString()))
				securitySchemeObject.setName(securityScheme.name());
		}
		if (StringUtils.isNotBlank(securityScheme.paramName()))
			securitySchemeObject.setName(securityScheme.paramName());

		 if (securityScheme.extensions().length > 0) {
			 Map extensions = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), securityScheme.extensions());
			 if (propertyResolverUtils.isResolveExtensionsProperties()) {
				 Map extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
				 extensionsResolved.forEach(securitySchemeObject::addExtension);
			 }
			 else {
				 extensions.forEach(securitySchemeObject::addExtension);
			 }
		 }
		 
		getOAuthFlows(securityScheme.flows(), locale).ifPresent(securitySchemeObject::setFlows);

		SecuritySchemePair result = new SecuritySchemePair(key, securitySchemeObject);
		return Optional.of(result);
	}

	/**
	 * Build security requirement.
	 *
	 * @param securityRequirements the security requirements
	 * @param operation the operation
	 */
	public void buildSecurityRequirement(
			io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements, Operation operation) {
		Optional> requirementsObject = this.getSecurityRequirements(securityRequirements);
		requirementsObject.ifPresent(requirements -> requirements.stream()
				.filter(r -> operation.getSecurity() == null || !operation.getSecurity().contains(r))
				.forEach(operation::addSecurityItem));
	}

	/**
	 * Gets o auth flows.
	 *
	 * @param oAuthFlows the o auth flows
	 * @param locale the locale
	 * @return the o auth flows
	 */
	private Optional getOAuthFlows(io.swagger.v3.oas.annotations.security.OAuthFlows oAuthFlows, Locale locale) {
		if (isEmpty(oAuthFlows))
			return Optional.empty();

		OAuthFlows oAuthFlowsObject = new OAuthFlows();
		if (oAuthFlows.extensions().length > 0) {
			Map extensions = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), oAuthFlows.extensions());
			if (propertyResolverUtils.isResolveExtensionsProperties()) {
				Map extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
				extensionsResolved.forEach(oAuthFlowsObject::addExtension);
			}
			else {
				extensions.forEach(oAuthFlowsObject::addExtension);
			}
		}
		getOAuthFlow(oAuthFlows.authorizationCode(), locale).ifPresent(oAuthFlowsObject::setAuthorizationCode);
		getOAuthFlow(oAuthFlows.clientCredentials(), locale).ifPresent(oAuthFlowsObject::setClientCredentials);
		getOAuthFlow(oAuthFlows.implicit(), locale).ifPresent(oAuthFlowsObject::setImplicit);
		getOAuthFlow(oAuthFlows.password(), locale).ifPresent(oAuthFlowsObject::setPassword);
		return Optional.of(oAuthFlowsObject);
	}

	/**
	 * Gets o auth flow.
	 *
	 * @param oAuthFlow the o auth flow
	 * @param locale the locale
	 * @return the o auth flow
	 */
	private Optional getOAuthFlow(io.swagger.v3.oas.annotations.security.OAuthFlow oAuthFlow, Locale locale) {
		if (isEmpty(oAuthFlow)) {
			return Optional.empty();
		}
		OAuthFlow oAuthFlowObject = new OAuthFlow();
		if (StringUtils.isNotBlank(oAuthFlow.authorizationUrl()))
			oAuthFlowObject.setAuthorizationUrl(propertyResolverUtils.resolve(oAuthFlow.authorizationUrl(), locale));

		if (StringUtils.isNotBlank(oAuthFlow.refreshUrl()))
			oAuthFlowObject.setRefreshUrl(propertyResolverUtils.resolve(oAuthFlow.refreshUrl(), locale));

		if (StringUtils.isNotBlank(oAuthFlow.tokenUrl()))
			oAuthFlowObject.setTokenUrl(propertyResolverUtils.resolve(oAuthFlow.tokenUrl(), locale));

		if (oAuthFlow.extensions().length > 0) {
			Map extensions = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), oAuthFlow.extensions());
			if (propertyResolverUtils.isResolveExtensionsProperties()) {
				Map extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
				extensionsResolved.forEach(oAuthFlowObject::addExtension);
			}
			else {
				extensions.forEach(oAuthFlowObject::addExtension);
			}
		}
		getScopes(oAuthFlow.scopes()).ifPresent(oAuthFlowObject::setScopes);
		return Optional.of(oAuthFlowObject);
	}

	/**
	 * Gets scopes.
	 *
	 * @param scopes the scopes
	 * @return the scopes
	 */
	private Optional getScopes(OAuthScope[] scopes) {
		Scopes scopesObject = new Scopes();
		Arrays.stream(scopes).forEach(scope -> scopesObject.addString(scope.name(), scope.description()));
		return Optional.of(scopesObject);
	}

	/**
	 * Gets in.
	 *
	 * @param value the value
	 * @return the in
	 */
	private SecurityScheme.In getIn(String value) {
		return Arrays.stream(SecurityScheme.In.values()).filter(i -> i.toString().equals(value)).findFirst()
				.orElse(null);
	}

	/**
	 * Gets type.
	 *
	 * @param value the value
	 * @return the type
	 */
	private SecurityScheme.Type getType(String value) {
		return Arrays.stream(SecurityScheme.Type.values()).filter(i -> i.toString().equals(value)).findFirst()
				.orElse(null);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy