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

tapir.docs.openapi.SecuritySchemesForEndpoints.scala Maven / Gradle / Ivy

There is a newer version: 0.8.11
Show newest version
package tapir.docs.openapi

import tapir.internal._
import tapir.openapi.{OAuthFlow, OAuthFlows, SecurityScheme}
import tapir.{Endpoint, EndpointIO, EndpointInput}

import scala.annotation.tailrec

private[openapi] object SecuritySchemesForEndpoints {

  def apply(es: Iterable[Endpoint[_, _, _, _]]): SecuritySchemes = {
    val auths = es.flatMap(e => e.input.auths)
    val authSecuritySchemes = auths.map(a => (a, authToSecurityScheme(a)))
    val securitySchemes = authSecuritySchemes.map(_._2).toSet
    val namedSecuritySchemes = nameSecuritySchemes(securitySchemes.toVector, Set(), Map())

    authSecuritySchemes.map { case (a, s) => a -> ((namedSecuritySchemes(s), s)) }.toMap
  }

  @tailrec
  private def nameSecuritySchemes(
      schemes: Vector[SecurityScheme],
      takenNames: Set[SchemeName],
      acc: Map[SecurityScheme, SchemeName]
  ): Map[SecurityScheme, SchemeName] = {
    schemes match {
      case Vector() => acc
      case scheme +: tail =>
        val baseName = scheme.`type` + "Auth"
        val name = uniqueName(baseName, !takenNames.contains(_))
        nameSecuritySchemes(tail, takenNames + name, acc + (scheme -> name))
    }
  }

  private def authToSecurityScheme(a: EndpointInput.Auth[_]): SecurityScheme = a match {
    case EndpointInput.Auth.ApiKey(input) =>
      val (name, in) = apiKeyInputNameAndIn(input.asVectorOfBasicInputs())
      SecurityScheme("apiKey", None, Some(name), Some(in), None, None, None, None)
    case EndpointInput.Auth.Http(scheme, _) =>
      SecurityScheme("http", None, None, None, Some(scheme.toLowerCase()), None, None, None)
    case EndpointInput.Auth.Oauth2(authorizationUrl, tokenUrl, scopes, refreshUrl, _) =>
      SecurityScheme(
        "oauth2",
        None,
        Some("Oauth2"),
        Some("header"),
        Some("bearer"),
        None,
        Some(OAuthFlows(authorizationCode = Some(OAuthFlow(authorizationUrl, tokenUrl, refreshUrl, scopes)))),
        None
      )
  }

  private def apiKeyInputNameAndIn(input: Vector[EndpointInput.Basic[_]]) = input match {
    case Vector(EndpointIO.Header(name, _, _))    => (name, "header")
    case Vector(EndpointInput.Query(name, _, _))  => (name, "query")
    case Vector(EndpointInput.Cookie(name, _, _)) => (name, "cookie")
    case _                                        => throw new IllegalArgumentException(s"Api key authentication can only be read from headers, queries or cookies, not: $input")
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy