Skip to content

Commit f9d5840

Browse files
committed
feat: refactoring filters to middlewares
1 parent b3870c7 commit f9d5840

File tree

6 files changed

+99
-59
lines changed

6 files changed

+99
-59
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package de.innfactory.bootstrapplay2.filters
2+
3+
import de.innfactory.bootstrapplay2.filters.access.RouteBlacklistFilter
4+
import de.innfactory.bootstrapplay2.filters.logging.AccessLoggingFilter
5+
import de.innfactory.bootstrapplay2.filters.tracing.TracingFilter
6+
import de.innfactory.smithy4play.middleware.{MiddlewareBase, MiddlewareRegistryBase, ValidateAuthMiddleware}
7+
8+
import javax.inject.Inject
9+
10+
class MiddlewareRegistry @Inject() (
11+
routeBlacklistFilter: RouteBlacklistFilter,
12+
accessLoggingFilter: AccessLoggingFilter,
13+
tracingFilter: TracingFilter,
14+
validateAuthMiddleware: ValidateAuthMiddleware
15+
) extends MiddlewareRegistryBase {
16+
override val middlewares: Seq[MiddlewareBase] =
17+
Seq(tracingFilter, routeBlacklistFilter, accessLoggingFilter, validateAuthMiddleware)
18+
}

app/de/innfactory/bootstrapplay2/filters/access/RouteBlacklistFilter.scala

+14-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package de.innfactory.bootstrapplay2.filters.access
33
import javax.inject.Inject
44
import akka.stream.Materializer
55
import com.typesafe.config.Config
6+
import de.innfactory.smithy4play
7+
import de.innfactory.smithy4play.{RouteResult, RoutingContext}
8+
import de.innfactory.smithy4play.middleware.MiddlewareBase
69
import play.api.mvc.Results.NotFound
710

811
import scala.concurrent.ExecutionContext.Implicits.global
@@ -13,7 +16,7 @@ import play.api.mvc._
1316
import play.api._
1417

1518
class RouteBlacklistFilter @Inject() (config: Config, implicit val mat: Materializer, environment: Environment)
16-
extends Filter {
19+
extends MiddlewareBase {
1720

1821
case class BlackListEntry(route: String, environment: Mode, method: String)
1922

@@ -23,22 +26,20 @@ class RouteBlacklistFilter @Inject() (config: Config, implicit val mat: Material
2326

2427
/**
2528
* Check if route is contained in blacklistedRoutes and block request if true
26-
* @param nextFilter
27-
* @param request
28-
* @return
2929
*/
30-
def apply(nextFilter: RequestHeader => Future[Result])(request: RequestHeader): Future[Result] =
31-
if (shouldBeBlocked(request.path, request.method)) {
32-
accessLogger.logger.warn(s"Illegal access to ${request.path} with ${request.method} in production")
33-
Future(NotFound(""))
34-
} else
35-
nextFilter.apply(request)
36-
37-
def shouldBeBlocked(path: String, method: String): Boolean = {
30+
override protected def skipMiddleware(r: RoutingContext): Boolean = {
31+
val path = r.requestHeader.path
32+
val method = r.requestHeader.method
3833
for (route <- blacklistedRoutes)
3934
if (environment.mode == route.environment && path.startsWith(route.route) && route.method == method)
40-
return true
35+
accessLogger.logger.warn(s"Illegal access to $path with $method in production")
36+
return true
4137
false
4238
}
4339

40+
override protected def logic(
41+
r: RoutingContext,
42+
next: RoutingContext => RouteResult[smithy4play.EndpointResult]
43+
): RouteResult[smithy4play.EndpointResult] =
44+
next.apply(r)
4445
}

app/de/innfactory/bootstrapplay2/filters/logging/AccessLoggingFilter.scala

+17-15
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,49 @@ package de.innfactory.bootstrapplay2.filters.logging
33
import javax.inject.Inject
44
import akka.stream.Materializer
55
import com.typesafe.config.Config
6+
import de.innfactory.smithy4play
7+
import de.innfactory.smithy4play.{ContextRouteError, EndpointResult, RouteResult, RoutingContext}
8+
import de.innfactory.smithy4play.middleware.MiddlewareBase
69

710
import scala.concurrent.ExecutionContext.Implicits.global
811
import scala.concurrent.Future
912
import play.api.Logger
1013
import play.api.mvc._
1114
import play.api._
1215

13-
class AccessLoggingFilter @Inject() (config: Config, implicit val mat: Materializer) extends Filter {
16+
class AccessLoggingFilter @Inject() (config: Config, implicit val mat: Materializer) extends MiddlewareBase {
1417
val accessLogger = Logger("AccessFilterLog")
1518

1619
/**
1720
* status list from application.conf
1821
*/
1922
private val configAccessStatus =
20-
config.getIntList("logging.access.statusList ")
23+
config.getIntList("logging.access.statusList")
2124

2225
/**
2326
* Logs requests if result header status is inclueded in logging.access.statusList as defined in application.conf
24-
* @param next
25-
* @param request
26-
* @return
2727
*/
28-
def apply(next: RequestHeader => Future[Result])(request: RequestHeader): Future[Result] = {
29-
val resultFuture = next(request)
30-
resultFuture.foreach { result =>
31-
if (shouldBeLogged(result)) {
28+
override protected def logic(
29+
r: RoutingContext,
30+
next: RoutingContext => RouteResult[EndpointResult]
31+
): RouteResult[EndpointResult] = {
32+
val request = r.requestHeader
33+
val result = next(r)
34+
result.leftMap { e =>
35+
if (shouldBeLogged(e)) {
3236
val msg =
33-
s"RequestID: status=${result.header.status} method=${request.method} uri=${request.uri} remote-address=${request.remoteAddress}" +
37+
s"RequestID: status=${e.statusCode} method=${request.method} uri=${request.uri} remote-address=${request.remoteAddress}" +
3438
s" authorization-header=${request.headers.get("Authorization")}"
3539
accessLogger.warn(msg)
3640
}
41+
e
3742
}
38-
resultFuture
3943
}
4044

4145
/**
4246
* check if request/result should be logged
43-
* @param result
44-
* @return
4547
*/
46-
def shouldBeLogged(result: Result): Boolean =
47-
configAccessStatus.contains(result.header.status)
48+
def shouldBeLogged(e: ContextRouteError): Boolean =
49+
configAccessStatus.contains(e.statusCode)
4850

4951
}

app/de/innfactory/bootstrapplay2/filters/TracingFilter.scala app/de/innfactory/bootstrapplay2/filters/tracing/TracingFilter.scala

+48-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package de.innfactory.bootstrapplay2.filters
1+
package de.innfactory.bootstrapplay2.filters.tracing
22

33
import akka.stream.Materializer
44
import com.google.cloud.opentelemetry.shadow.semconv.trace.attributes.SemanticAttributes
@@ -11,6 +11,9 @@ import de.innfactory.play.tracing.GoogleTracingIdentifier.GoogleAttributes.{
1111
}
1212
import de.innfactory.play.tracing.GoogleTracingIdentifier.XTRACINGID
1313
import de.innfactory.play.tracing.{OpentelemetryProvider, TraceLogger}
14+
import de.innfactory.smithy4play
15+
import de.innfactory.smithy4play.middleware.MiddlewareBase
16+
import de.innfactory.smithy4play.{EndpointResult, RouteResult, RoutingContext}
1417
import io.opentelemetry.api.GlobalOpenTelemetry
1518
import io.opentelemetry.context.Context
1619
import org.joda.time.DateTime
@@ -19,21 +22,30 @@ import play.api.mvc._
1922

2023
import javax.inject.Inject
2124
import scala.concurrent.ExecutionContext.Implicits.global
22-
import scala.concurrent.Future
2325

24-
class TracingFilter @Inject() (config: Config, implicit val mat: Materializer) extends Filter with ImplicitLogContext {
26+
class TracingFilter @Inject() (config: Config, implicit val mat: Materializer)
27+
extends MiddlewareBase
28+
with ImplicitLogContext {
2529
private val healthEndpoints: Map[String, Seq[String]] = Map("GET" -> Seq("/", "/liveness", "/readiness"))
2630
private val healthLogger = Logger("health").logger
2731
private val configAccessStatus = config.getIntList("logging.access.statusList")
2832

29-
def apply(next: RequestHeader => Future[Result])(request: RequestHeader): Future[Result] =
30-
if (!healthEndpoints.getOrElse(request.method, Seq.empty).contains(request.path))
31-
handleLoggingAndTracing(next)(request)
32-
else
33-
handleHealthChecksLogging(next, request)
34-
35-
def handleLoggingAndTracing(next: RequestHeader => Future[Result])(request: RequestHeader): Future[Result] = {
33+
override protected def logic(
34+
r: RoutingContext,
35+
next: RoutingContext => RouteResult[smithy4play.EndpointResult]
36+
): RouteResult[smithy4play.EndpointResult] = if (
37+
!healthEndpoints.getOrElse(r.requestHeader.method, Seq.empty).contains(r.requestHeader.path)
38+
)
39+
handleLoggingAndTracing(next, r)
40+
else
41+
handleHealthChecksLogging(next, r)
42+
43+
def handleLoggingAndTracing(
44+
next: RoutingContext => RouteResult[smithy4play.EndpointResult],
45+
r: RoutingContext
46+
): RouteResult[smithy4play.EndpointResult] = {
3647
// Start Trace Span Root
48+
val request = r.requestHeader
3749
val span = OpentelemetryProvider.getTracer().spanBuilder(request.path).startSpan()
3850
val latencyRecorder = OpentelemetryProvider
3951
.getMeter()
@@ -68,21 +80,25 @@ class TracingFilter @Inject() (config: Config, implicit val mat: Materializer) e
6880

6981
map.addOne(xTracingId)
7082

83+
// Check Start Time
84+
val start = DateTime.now
85+
7186
// Call Next Filter with new Headers
72-
val result = next(request.withHeaders(request.headers.add(map.toList: _*)))
87+
val result = next(
88+
r.copy(
89+
requestHeader = request.withHeaders(request.headers.add(map.toList: _*))
90+
)
91+
)
7392

7493
val logger = new TraceLogger(Some(span))
7594
val msg: String = logStart(request, xTracingId)(logger)
7695

77-
// Check Start Time
78-
val start = DateTime.now
79-
8096
// Process Result
8197
result.map { res =>
8298
// Add more Attributes to trace
83-
span.setAttribute(HTTP_STATUS_CODE, res.header.status)
84-
span.setAttribute(STATUS, res.header.status)
85-
span.setAttribute(HTTP_RESPONSE_SIZE, res.body.contentLength.getOrElse(0L))
99+
span.setAttribute(HTTP_STATUS_CODE, res.code)
100+
span.setAttribute(STATUS, res.code)
101+
span.setAttribute(HTTP_RESPONSE_SIZE, res.body.map(_.length).getOrElse(0))
86102

87103
// Finish Root Span
88104
span.end()
@@ -96,17 +112,21 @@ class TracingFilter @Inject() (config: Config, implicit val mat: Materializer) e
96112
logResult(xTracingId, msg, res, request)(logger)
97113

98114
// Add xTracingId to Result Header
99-
res.withHeaders(xTracingId)
115+
res.copy(
116+
headers = res.headers + xTracingId
117+
)
100118
}
101119

102120
}
103121

104-
private def handleHealthChecksLogging(next: RequestHeader => Future[Result], request: RequestHeader) = {
105-
// Handle Health Check Logging
106-
val result = next(request)
107-
result.map { res =>
108-
if (res.header.status != 200) healthLogger.error(s"[HealthCheck] FAILED with ${res.header.status}")
109-
res
122+
private def handleHealthChecksLogging(
123+
next: RoutingContext => RouteResult[smithy4play.EndpointResult],
124+
r: RoutingContext
125+
): RouteResult[smithy4play.EndpointResult] = {
126+
val result = next.apply(r)
127+
result.leftMap { e =>
128+
if (e.statusCode != 200) healthLogger.error(s"[HealthCheck] FAILED with ${e.statusCode}")
129+
e
110130
}
111131
}
112132

@@ -118,12 +138,11 @@ class TracingFilter @Inject() (config: Config, implicit val mat: Materializer) e
118138
msg
119139
}
120140

121-
private def logResult(xTracingId: (String, String), msg: String, res: Result, req: RequestHeader)(
141+
private def logResult(xTracingId: (String, String), msg: String, res: EndpointResult, req: RequestHeader)(
122142
logger: TraceLogger
123143
): Unit =
124-
if (configAccessStatus.contains(res.header.status))
125-
logger.warn(s"[Trace-${xTracingId._2}] $msg status=${res.header.status} END")
144+
if (configAccessStatus.contains(res.code))
145+
logger.warn(s"[Trace-${xTracingId._2}] $msg status=${res.code} END")
126146
else
127-
logger.info(s"[Trace-${xTracingId._2}] $msg status=${res.header.status} END")
128-
147+
logger.info(s"[Trace-${xTracingId._2}] $msg status=${res.code} END")
129148
}

conf/application.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ play.http.secret.key = ${?PLAY_HTTP_SECRET_KEY}
6767

6868
// FILTERS
6969

70-
play.filters.enabled = ["de.innfactory.bootstrapplay2.filters.TracingFilter", "de.innfactory.bootstrapplay2.filters.logging.AccessLoggingFilter", "play.filters.cors.CORSFilter", "de.innfactory.bootstrapplay2.filters.access.RouteBlacklistFilter" ]
70+
play.filters.enabled = [ "play.filters.cors.CORSFilter" ]
7171

7272
play.filters.cors {
7373
pathPrefixes = ["/v1/"]

project/Dependencies.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ object Dependencies {
2525

2626
// innFactory Utils
2727
val scalaUtil = "de.innfactory.scala-utils" %% "scala-utils" % "2.0.1"
28-
val smithy4play = "de.innfactory" %% "smithy4play" % "0.4.4-alpha"
28+
val smithy4play = "de.innfactory" %% "smithy4play" % "0.4.4-beta"
2929

3030
// Prod
3131
val slickPg = "com.github.tminglei" %% "slick-pg" % "0.20.4"

0 commit comments

Comments
 (0)