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

play.filters.cors.CORSConfig.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 
 */

package play.filters.cors

import scala.concurrent.duration._

import play.api.Configuration
import play.filters.cors.CORSConfig.Origins

/**
 * Configuration for play.filters.cors.AbstractCORSPolicy.
 *
 * 
    *
  • allow only requests with origins from a whitelist (by default all origins are allowed)
  • *
  • allow only HTTP methods from a whitelist for preflight requests (by default all methods are allowed)
  • *
  • allow only HTTP headers from a whitelist for preflight requests (by default all headers are allowed)
  • *
  • set custom HTTP headers to be exposed in the response (by default no headers are exposed)
  • *
  • disable/enable support for credentials (by default credentials support is enabled)
  • *
  • set how long (in seconds) the results of a preflight request can be cached in a preflight result cache (by default 3600 seconds, 1 hour)
  • *
  • enable/disable serving requests with origins not in whitelist as non-CORS requests (by default they are forbidden)
  • *
* * @param allowedOrigins * [[http://www.w3.org/TR/cors/#resource-requests §6.1.2]] * [[http://www.w3.org/TR/cors/#resource-preflight-requests §6.2.2]] * Always matching is acceptable since the list of origins can be unbounded. * @param isHttpMethodAllowed * [[http://www.w3.org/TR/cors/#resource-preflight-requests §6.2.5]] * Always matching is acceptable since the list of methods can be unbounded. * @param isHttpHeaderAllowed * [[http://www.w3.org/TR/cors/#resource-preflight-requests §6.2.6]] * Always matching is acceptable since the list of headers can be unbounded. * @param exposedHeaders * [[https://www.w3.org/TR/cors/#list-of-exposed-headers §6.1.4]] * By not adding the appropriate headers resource can also clear the preflight result cache of all entries * where origin is a case-sensitive match for the value of the Origin header and url is a case-sensitive match for the URL of the resource. * @param supportsCredentials * [[https://www.w3.org/TR/cors/#supports-credentials §6.1.3]] * The string "*" cannot be used for a resource that supports credentials. * @param preflightMaxAge * [[http://www.w3.org/TR/cors/#resource-preflight-requests §6.2.8]] * Set how long the user agent is allowed to cache the result of the preflight request. * @param serveForbiddenOrigins * Enable/disable serving requests with origins not in whitelist as non-CORS requests. */ case class CORSConfig( allowedOrigins: Origins = Origins.None, isHttpMethodAllowed: String => Boolean = _ => true, isHttpHeaderAllowed: String => Boolean = _ => true, exposedHeaders: Seq[String] = Seq.empty, supportsCredentials: Boolean = true, preflightMaxAge: Duration = 1.hour, serveForbiddenOrigins: Boolean = false ) { def anyOriginAllowed: Boolean = allowedOrigins == Origins.All def withAnyOriginAllowed: CORSConfig = withOriginsAllowed(Origins.All) def withOriginsAllowed(origins: String => Boolean): CORSConfig = copy(allowedOrigins = Origins.Matching(origins)) private[play] def allowedForOrigin(origin: String): Option[String] = allowedOrigins match { case Origins.All if supportsCredentials => Some(origin) case Origins.All => Some("*") case Origins.Matching(fn) if fn(origin) => Some(origin) case Origins.Matching(fn) if fn("*") => Some("*") case _ => None } def withMethodsAllowed(methods: String => Boolean): CORSConfig = copy(isHttpMethodAllowed = methods) def withHeadersAllowed(headers: String => Boolean): CORSConfig = copy(isHttpHeaderAllowed = headers) def withExposedHeaders(headers: Seq[String]): CORSConfig = copy(exposedHeaders = headers) def withCredentialsSupport(supportsCredentials: Boolean): CORSConfig = copy(supportsCredentials = supportsCredentials) def withPreflightMaxAge(maxAge: Duration): CORSConfig = copy(preflightMaxAge = maxAge) def withServeForbiddenOrigins(serveForbiddenOrigins: Boolean): CORSConfig = copy(serveForbiddenOrigins = serveForbiddenOrigins) import java.util.{ function => juf } import scala.jdk.CollectionConverters._ import scala.jdk.FunctionConverters._ def withOriginsAllowed(origins: juf.Function[String, Boolean]): CORSConfig = withOriginsAllowed(origins.asScala) def withMethodsAllowed(methods: juf.Function[String, Boolean]): CORSConfig = withMethodsAllowed(methods.asScala) def withHeadersAllowed(headers: juf.Function[String, Boolean]): CORSConfig = withHeadersAllowed(headers.asScala) def withExposedHeaders(headers: java.util.List[String]): CORSConfig = withExposedHeaders(headers.asScala.toSeq) def withPreflightMaxAge(maxAge: java.time.Duration): CORSConfig = withPreflightMaxAge(Duration.fromNanos(maxAge.toNanos)) } /** * Helpers to build CORS policy configurations */ object CORSConfig { /** * Origins allowed by the CORS filter */ sealed trait Origins extends (String => Boolean) object Origins { case object All extends Origins { override def apply(v: String) = true } case class Matching(func: String => Boolean) extends Origins { override def apply(v: String) = func(v) } val None = Matching(_ => false) } /** */ val denyAll: CORSConfig = CORSConfig( allowedOrigins = Origins.None, isHttpMethodAllowed = _ => false, isHttpHeaderAllowed = _ => false, exposedHeaders = Seq.empty, supportsCredentials = true, preflightMaxAge = 0.seconds, serveForbiddenOrigins = false ) /** * Builds a [[CORSConfig]] from a play.api.Configuration instance. * * @example The configuration is as follows: * {{{ * play.filters.cors { * pathPrefixes = ["/myresource", ...] # ["/"] by default * allowedOrigins = ["http://...", ...] # If null, all origins are allowed * allowedHttpMethods = ["PATCH", ...] # If null, all methods are allowed * allowedHttpHeaders = ["Custom-Header", ...] # If null, all headers are allowed * exposedHeaders = [...] # empty by default * supportsCredentials = true # true by default * preflightMaxAge = 1 hour # 1 hour by default * serveForbiddenOrigins = false # false by default * } * * }}} */ def fromConfiguration(conf: Configuration): CORSConfig = { val config = conf.get[Configuration]("play.filters.cors") fromUnprefixedConfiguration(config) } private[cors] def fromUnprefixedConfiguration(config: Configuration): CORSConfig = { CORSConfig( allowedOrigins = config.get[Option[Seq[String]]]("allowedOrigins") match { case Some(allowed) => Origins.Matching(allowed.toSet) case None => Origins.All }, isHttpMethodAllowed = config .get[Option[Seq[String]]]("allowedHttpMethods") .map { methods => val s = methods.toSet s.contains _ } .getOrElse(_ => true), isHttpHeaderAllowed = config .get[Option[Seq[String]]]("allowedHttpHeaders") .map { headers => val s = headers.map(_.toLowerCase(java.util.Locale.ENGLISH)).toSet s.contains _ } .getOrElse(_ => true), exposedHeaders = config.get[Seq[String]]("exposedHeaders"), supportsCredentials = config.get[Boolean]("supportsCredentials"), preflightMaxAge = config.get[Duration]("preflightMaxAge"), serveForbiddenOrigins = config.get[Boolean]("serveForbiddenOrigins") ) } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy