aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/core/rest/directives
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/core/rest/directives')
-rw-r--r--src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala19
-rw-r--r--src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala72
-rw-r--r--src/main/scala/xyz/driver/core/rest/directives/Directives.scala6
-rw-r--r--src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala85
-rw-r--r--src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala40
5 files changed, 0 insertions, 222 deletions
diff --git a/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala b/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala
deleted file mode 100644
index ff3424d..0000000
--- a/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package xyz.driver.core
-package rest
-package directives
-
-import akka.http.scaladsl.server.{Directive1, Directives => AkkaDirectives}
-import xyz.driver.core.auth.{Permission, User}
-import xyz.driver.core.rest.auth.AuthProvider
-
-/** Authentication and authorization directives. */
-trait AuthDirectives extends AkkaDirectives {
-
- /** Authenticate a user based on service request headers and check if they have all given permissions. */
- def authenticateAndAuthorize[U <: User](
- authProvider: AuthProvider[U],
- permissions: Permission*): Directive1[AuthorizedServiceRequestContext[U]] = {
- authProvider.authorize(permissions: _*)
- }
-
-}
diff --git a/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala b/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala
deleted file mode 100644
index 5a6bbfd..0000000
--- a/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala
+++ /dev/null
@@ -1,72 +0,0 @@
-package xyz.driver.core
-package rest
-package directives
-
-import akka.http.scaladsl.model.HttpMethods._
-import akka.http.scaladsl.model.headers._
-import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
-import akka.http.scaladsl.server.{Route, Directives => AkkaDirectives}
-
-/** Directives to handle Cross-Origin Resource Sharing (CORS). */
-trait CorsDirectives extends AkkaDirectives {
-
- /** Route handler that injects Cross-Origin Resource Sharing (CORS) headers depending on the request
- * origin.
- *
- * In a microservice environment, it can be difficult to know in advance the exact origin
- * from which requests may be issued [1]. For example, the request may come from a web page served from
- * any of the services, on any namespace or from other documentation sites. In general, only a set
- * of domain suffixes can be assumed to be known in advance. Unfortunately however, browsers that
- * implement CORS require exact specification of allowed origins, including full host name and scheme,
- * in order to send credentials and headers with requests to other origins.
- *
- * This route wrapper provides a simple way alleviate CORS' exact allowed-origin requirement by
- * dynamically echoing the origin as an allowed origin if and only if its domain is whitelisted.
- *
- * Note that the simplicity of this implementation comes with two notable drawbacks:
- *
- * - All OPTION requests are "hijacked" and will not be passed to the inner route of this wrapper.
- *
- * - Allowed methods and headers can not be customized on a per-request basis. All standard
- * HTTP methods are allowed, and allowed headers are specified for all inner routes.
- *
- * This handler is not suited for cases where more fine-grained control of responses is required.
- *
- * [1] Assuming browsers communicate directly with the services and that requests aren't proxied through
- * a common gateway.
- *
- * @param allowedSuffixes The set of domain suffixes (e.g. internal.example.org, example.org) of allowed
- * origins.
- * @param allowedHeaders Header names that will be set in `Access-Control-Allow-Headers`.
- * @param inner Route into which CORS headers will be injected.
- */
- def cors(allowedSuffixes: Set[String], allowedHeaders: Seq[String])(inner: Route): Route = {
- optionalHeaderValueByType[Origin](()) { maybeOrigin =>
- val allowedOrigins: HttpOriginRange = maybeOrigin match {
- // Note that this is not a security issue: the client will never send credentials if the allowed
- // origin is set to *. This case allows us to deal with clients that do not send an origin header.
- case None => HttpOriginRange.*
- case Some(requestOrigin) =>
- val allowedOrigin = requestOrigin.origins.find(origin =>
- allowedSuffixes.exists(allowed => origin.host.host.address endsWith allowed))
- allowedOrigin.map(HttpOriginRange(_)).getOrElse(HttpOriginRange.*)
- }
-
- respondWithHeaders(
- `Access-Control-Allow-Origin`.forRange(allowedOrigins),
- `Access-Control-Allow-Credentials`(true),
- `Access-Control-Allow-Headers`(allowedHeaders: _*),
- `Access-Control-Expose-Headers`(allowedHeaders: _*)
- ) {
- options { // options is used during preflight check
- complete(
- HttpResponse(StatusCodes.OK)
- .withHeaders(`Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE, PATCH, TRACE)))
- } ~ inner // in case of non-preflight check we don't do anything special
- }
- }
- }
-
-}
-
-object CorsDirectives extends CorsDirectives
diff --git a/src/main/scala/xyz/driver/core/rest/directives/Directives.scala b/src/main/scala/xyz/driver/core/rest/directives/Directives.scala
deleted file mode 100644
index 0cd4ef1..0000000
--- a/src/main/scala/xyz/driver/core/rest/directives/Directives.scala
+++ /dev/null
@@ -1,6 +0,0 @@
-package xyz.driver.core
-package rest
-package directives
-
-trait Directives extends AuthDirectives with CorsDirectives with PathMatchers with Unmarshallers
-object Directives extends Directives
diff --git a/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala b/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala
deleted file mode 100644
index 218c9ae..0000000
--- a/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala
+++ /dev/null
@@ -1,85 +0,0 @@
-package xyz.driver.core
-package rest
-package directives
-
-import java.time.Instant
-import java.util.UUID
-
-import akka.http.scaladsl.model.Uri.Path
-import akka.http.scaladsl.server.PathMatcher.{Matched, Unmatched}
-import akka.http.scaladsl.server.{PathMatcher, PathMatcher1, PathMatchers => AkkaPathMatchers}
-import eu.timepit.refined.collection.NonEmpty
-import eu.timepit.refined.refineV
-import xyz.driver.core.domain.PhoneNumber
-import xyz.driver.core.time.Time
-
-import scala.util.control.NonFatal
-
-/** Akka-HTTP path matchers for custom core types. */
-trait PathMatchers {
-
- private def UuidInPath[T]: PathMatcher1[Id[T]] =
- AkkaPathMatchers.JavaUUID.map((id: UUID) => Id[T](id.toString.toLowerCase))
-
- def IdInPath[T]: PathMatcher1[Id[T]] = UuidInPath[T] | new PathMatcher1[Id[T]] {
- def apply(path: Path) = path match {
- case Path.Segment(segment, tail) => Matched(tail, Tuple1(Id[T](segment)))
- case _ => Unmatched
- }
- }
-
- def NameInPath[T]: PathMatcher1[Name[T]] = new PathMatcher1[Name[T]] {
- def apply(path: Path) = path match {
- case Path.Segment(segment, tail) => Matched(tail, Tuple1(Name[T](segment)))
- case _ => Unmatched
- }
- }
-
- private def timestampInPath: PathMatcher1[Long] =
- PathMatcher("""[+-]?\d*""".r) flatMap { string =>
- try Some(string.toLong)
- catch { case _: IllegalArgumentException => None }
- }
-
- def InstantInPath: PathMatcher1[Instant] =
- new PathMatcher1[Instant] {
- def apply(path: Path): PathMatcher.Matching[Tuple1[Instant]] = path match {
- case Path.Segment(head, tail) =>
- try Matched(tail, Tuple1(Instant.parse(head)))
- catch {
- case NonFatal(_) => Unmatched
- }
- case _ => Unmatched
- }
- } | timestampInPath.map(Instant.ofEpochMilli)
-
- def TimeInPath: PathMatcher1[Time] = InstantInPath.map(instant => Time(instant.toEpochMilli))
-
- def NonEmptyNameInPath[T]: PathMatcher1[NonEmptyName[T]] = new PathMatcher1[NonEmptyName[T]] {
- def apply(path: Path) = path match {
- case Path.Segment(segment, tail) =>
- refineV[NonEmpty](segment) match {
- case Left(_) => Unmatched
- case Right(nonEmptyString) => Matched(tail, Tuple1(NonEmptyName[T](nonEmptyString)))
- }
- case _ => Unmatched
- }
- }
-
- def RevisionInPath[T]: PathMatcher1[Revision[T]] =
- PathMatcher("""[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}""".r) flatMap { string =>
- Some(Revision[T](string))
- }
-
- def PhoneInPath: PathMatcher1[PhoneNumber] = new PathMatcher1[PhoneNumber] {
- def apply(path: Path) = path match {
- case Path.Segment(segment, tail) =>
- PhoneNumber
- .parse(segment)
- .map(parsed => Matched(tail, Tuple1(parsed)))
- .getOrElse(Unmatched)
- case _ => Unmatched
- }
- }
-
-}
diff --git a/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala b/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala
deleted file mode 100644
index 6c45d15..0000000
--- a/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala
+++ /dev/null
@@ -1,40 +0,0 @@
-package xyz.driver.core
-package rest
-package directives
-
-import java.util.UUID
-
-import akka.http.scaladsl.marshalling.{Marshaller, Marshalling}
-import akka.http.scaladsl.unmarshalling.Unmarshaller
-import spray.json.{JsString, JsValue, JsonParser, JsonReader, JsonWriter}
-
-/** Akka-HTTP unmarshallers for custom core types. */
-trait Unmarshallers {
-
- implicit def idUnmarshaller[A]: Unmarshaller[String, Id[A]] =
- Unmarshaller.strict[String, Id[A]] { str =>
- Id[A](UUID.fromString(str).toString)
- }
-
- implicit def paramUnmarshaller[T](implicit reader: JsonReader[T]): Unmarshaller[String, T] =
- Unmarshaller.firstOf(
- Unmarshaller.strict((JsString(_: String)) andThen reader.read),
- stringToValueUnmarshaller[T]
- )
-
- implicit def revisionFromStringUnmarshaller[T]: Unmarshaller[String, Revision[T]] =
- Unmarshaller.strict[String, Revision[T]](Revision[T])
-
- val jsValueToStringMarshaller: Marshaller[JsValue, String] =
- Marshaller.strict[JsValue, String](value => Marshalling.Opaque[String](() => value.compactPrint))
-
- def valueToStringMarshaller[T](implicit jsonFormat: JsonWriter[T]): Marshaller[T, String] =
- jsValueToStringMarshaller.compose[T](jsonFormat.write)
-
- val stringToJsValueUnmarshaller: Unmarshaller[String, JsValue] =
- Unmarshaller.strict[String, JsValue](value => JsonParser(value))
-
- def stringToValueUnmarshaller[T](implicit jsonFormat: JsonReader[T]): Unmarshaller[String, T] =
- stringToJsValueUnmarshaller.map[T](jsonFormat.read)
-
-}