package io.vertx.ext.web.api.contract.openapi3.impl;

import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.api.contract.RouterFactoryException;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Author:Francesco Guardiani @slinkydeveloper
/** * @author Francesco Guardiani @slinkydeveloper */
class SecurityHandlersStore { private static class SecurityRequirementKey { private String name; private String oauth2Scope; public SecurityRequirementKey(String name, String oauth2Scope) { this.name = name; this.oauth2Scope = oauth2Scope; } public SecurityRequirementKey(String name) { this(name, null); } public String getName() { return name; } public String getScope() { return oauth2Scope; } public boolean hasScope() { return oauth2Scope != null; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SecurityRequirementKey that = (SecurityRequirementKey) o; if (!name.equals(that.name)) return false; return Objects.equals(oauth2Scope, that.oauth2Scope); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + (oauth2Scope != null ? oauth2Scope.hashCode() : 0); return result; } private SecurityRequirementKey cloneWithoutScope() { return new SecurityRequirementKey(this.getName()); } } private final Map<SecurityRequirementKey, List<Handler<RoutingContext>>> securityHandlers; SecurityHandlersStore() { this.securityHandlers = new HashMap<>(); } protected void addSecurityRequirement(String name, String scope, Handler<RoutingContext> handler) { securityHandlers.computeIfAbsent(new SecurityRequirementKey(name, scope), k -> new ArrayList<>()).add(handler); } protected void addSecurityRequirement(String name, Handler<RoutingContext> handler) { securityHandlers.computeIfAbsent(new SecurityRequirementKey(name), k -> new ArrayList<>()).add(handler); } private List<Handler<RoutingContext>> mapWithFail(SecurityRequirementKey k) throws RouterFactoryException { if (k.hasScope()) return Optional .ofNullable((this.securityHandlers.get(k) != null) ? this.securityHandlers.get(k) : this.securityHandlers.get(k.cloneWithoutScope())) .orElseThrow(() -> RouterFactoryException.createMissingSecurityHandler(k.getName(), k.getScope())); else return Optional.ofNullable(this.securityHandlers.get(k)).orElseThrow(() -> RouterFactoryException.createMissingSecurityHandler(k.getName())); } private List<Handler<RoutingContext>> mapWithoutFail(SecurityRequirementKey k) { if (k.hasScope()) return Optional .ofNullable(this.securityHandlers.get(k)) .orElseGet(() -> this.securityHandlers.get(k.cloneWithoutScope())); else return this.securityHandlers.get(k); } protected List<Handler<RoutingContext>> solveSecurityHandlers(List<SecurityRequirement> nonTranslatedKeys, boolean failOnNotFound) { List<SecurityRequirementKey> keys = this.translateRequirements(nonTranslatedKeys); if (keys != null) { if (failOnNotFound) return keys.stream().map(this::mapWithFail).flatMap(Collection::stream).collect(Collectors.toList()); else return keys.stream().map(this::mapWithoutFail).filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList()); } else return new ArrayList<>(); } private List<SecurityRequirementKey> translateRequirements(List<SecurityRequirement> keys) { if (keys != null) return keys.stream() .flatMap(m -> m.entrySet().stream().flatMap(e -> { if (e.getValue() == null || e.getValue().size() == 0) return Stream.of(new SecurityRequirementKey(e.getKey())); else return e.getValue().stream().map(s -> new SecurityRequirementKey(e.getKey(), s)); })) .collect(Collectors.toList()); else return new ArrayList<>(); } }