Skip to content

Instantly share code, notes, and snippets.

@nyango
Last active December 24, 2019 06:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nyango/5e8dd6f9d0e8c3bf51c7a40899848308 to your computer and use it in GitHub Desktop.
Save nyango/5e8dd6f9d0e8c3bf51c7a40899848308 to your computer and use it in GitHub Desktop.
Play CORS Filter with wildcard origin matching
import javax.inject.{Inject, Singleton}
import akka.stream.Materializer
import play.api.Configuration
import play.filters.cors.CORSConfig.Origins.Matching
import play.filters.cors.{CORSConfig, CORSFilter}
import scala.concurrent.ExecutionContext
import scala.util.matching.Regex
@Singleton class WildcardAllowedCORSFilterFactory @Inject()(
implicit val mat: Materializer, exec: ExecutionContext, configuration: play.api.Configuration
) {
val wildcardAllowedOriginMatchers: Seq[String => Boolean] = configuration.getConfig("play.filters.cors")
.getOrElse(Configuration.empty).getStringSeq("allowedOrigins").getOrElse(Seq()).map { d =>
if (d.contains("*")) {
val regex: Regex = d.replace(".", "\\.").replace("*", ".*").r
(origin: String) => regex.pattern.matcher(origin).matches()
} else (origin: String) => d.equals(origin)
}
def getCorsFilter: CORSFilter = CORSFilter(corsConfig = CORSConfig.fromConfiguration(configuration).copy(allowedOrigins =
if (wildcardAllowedOriginMatchers.isEmpty) {
// @see [[https://www.w3.org/TR/cors/#resource-processing-model §6.2]]
// Always matching is acceptable since the list of origins can be unbounded.
All
} else {
// wildcard allowed matching
Matching(origin => wildcardAllowedOriginMatchers.exists(matcher => matcher(origin)))
}
))
}
@eliothxchan
Copy link

eliothxchan commented Nov 28, 2017

Thanks! This is pretty useful. In case anyone's also looking here, re-wrote this for use with Filter injection.

class Filters @Inject() (implicit val mat: Materializer, implicit val config: Configuration) extends HttpFilters {
  
  val originMatcher: Option[Seq[String => Boolean]] = for {
    filterConfiguration <- config.getConfig("play.filters.cors")
    allowedOrigins <- filterConfiguration.getStringSeq("allowedOrigins")
  } yield {
    allowedOrigins.map { d =>
      if (d.contains("*")) {
        val regex: Regex = d.replace(".", "\\.").replace("*", ".*").r
        (origin: String) => regex.pattern.matcher(origin).matches()
      } else (origin: String) => d.equals(origin)
    }
  }
  
  val matchingConfig = CORSConfig.fromConfiguration(config).copy(allowedOrigins = 
    originMatcher.map { resolvedMatcher =>
      Matching(origin => resolvedMatcher.exists(matcher => matcher(origin)))
    } getOrElse All
  )
  
  val filters = Seq(CORSFilter(corsConfig = matchingConfig))

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment