Skip to content

Commit 7dd66cb

Browse files
committed
fix remove refresh token bug + fix sendRefreshTokenToClientAsHeader + add companion for SessionEndpoints
1 parent 05729ee commit 7dd66cb

24 files changed

+350
-122
lines changed

server/testkit/src/main/scala/akka/http/scaladsl/testkit/PersistenceScalatestRouteTest.scala

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package akka.http.scaladsl.testkit
22

33
import akka.actor.ActorSystem
44
import akka.http.scaladsl.model.HttpHeader
5-
import akka.http.scaladsl.model.headers.{Cookie, HttpCookiePair, RawHeader}
5+
import akka.http.scaladsl.model.headers.{`Set-Cookie`, Cookie, HttpCookiePair, RawHeader}
66
import akka.http.scaladsl.server.directives.RouteDirectives
77
import akka.http.scaladsl.server.{ExceptionHandler, Route}
88
import app.softnetwork.api.server.scalatest.ServerTestKit
@@ -44,6 +44,10 @@ trait PersistenceScalatestRouteTest
4444

4545
lazy val routes: Route = mainRoutes(typedSystem())
4646

47+
@deprecated(
48+
"this method has been replaced by extractHeaders and will be removed",
49+
since = "0.3.1.1"
50+
)
4751
def extractCookies(headers: Seq[HttpHeader]): Seq[HttpHeader] = {
4852
headers
4953
.filter(header => {
@@ -58,20 +62,48 @@ trait PersistenceScalatestRouteTest
5862
if (value.isEmpty) {
5963
Seq.empty
6064
} else {
61-
var ret: Seq[HttpHeader] = Seq(Cookie(name, value))
62-
// required for akka-http-session
63-
if (name == "XSRF-TOKEN")
64-
ret = ret ++ Seq(RawHeader("X-XSRF-TOKEN", value))
65-
ret
65+
Seq(Cookie(name, value))
6666
}
6767
})
6868
}
6969

70+
@deprecated("this method has been replaced by findHeader and will be removed", since = "0.3.1.1")
7071
def findCookie(name: String): HttpHeader => Option[HttpCookiePair] = {
7172
case Cookie(cookies) => cookies.find(_.name == name)
7273
case _ => None
7374
}
7475

76+
def extractHeaders(headers: Seq[HttpHeader]): Seq[HttpHeader] =
77+
headers
78+
.flatMap {
79+
case header: `Set-Cookie` =>
80+
val cookie = header.value().split("=")
81+
val name = cookie.head
82+
val value = cookie.tail.mkString("=").split(";").head
83+
log.info(s"${header.name()}: ${header.value()}")
84+
if (value.isEmpty) {
85+
Seq.empty
86+
} else {
87+
Seq(Cookie(name, value))
88+
}
89+
case header: RawHeader =>
90+
val name = header.name
91+
val value = header.value
92+
log.info(s"$name: $value")
93+
if (value.isEmpty) {
94+
Seq.empty
95+
} else {
96+
Seq(RawHeader(name, value))
97+
}
98+
case _ => Seq.empty
99+
}
100+
101+
def findHeader(name: String): HttpHeader => Option[String] = {
102+
case Cookie(cookies) => cookies.find(_.name == name).map(_.value)
103+
case r: RawHeader if r.name == name => Some(r.value)
104+
case _ => None
105+
}
106+
75107
}
76108

77109
trait InMemoryPersistenceScalatestRouteTest

session/core/src/main/scala/app/softnetwork/session/persistence/typed/RefreshToken.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ trait RefreshTokenBehavior[T <: SessionData]
6868
RefreshTokenRemoved(cmd.selector)
6969
)
7070
.thenRun(_ => RefreshTokenRemoved(cmd.selector) ~> replyTo)
71-
.thenStop()
7271

7372
case _: LookupRefreshToken =>
7473
Effect.none.thenRun(_ =>

session/core/src/main/scala/app/softnetwork/session/service/SessionEndpoints.scala

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import akka.actor.typed.ActorSystem
44
import app.softnetwork.session.config.Settings.Session.CookieSecret
55
import app.softnetwork.session.handlers.SessionRefreshTokenDao
66
import com.softwaremill.session.{
7-
CsrfCheck,
87
GenericOneOffCookieSessionEndpoints,
98
GenericOneOffHeaderSessionEndpoints,
109
GenericRefreshableCookieSessionEndpoints,
@@ -21,7 +20,7 @@ import org.softnetwork.session.model.Session
2120
import scala.concurrent.ExecutionContext
2221

2322
trait SessionEndpoints extends GenericSessionEndpoints[Session] {
24-
_: SessionTransportEndpoints[Session] with SessionContinuityEndpoints[Session] with CsrfCheck =>
23+
_: SessionTransportEndpoints[Session] with SessionContinuityEndpoints[Session] =>
2524

2625
import Session._
2726

@@ -36,28 +35,45 @@ trait SessionEndpoints extends GenericSessionEndpoints[Session] {
3635
system
3736
)
3837

38+
def transport: SessionTransportEndpoints[Session] = this
39+
40+
def continuity: SessionContinuityEndpoints[Session] = this
3941
}
4042

41-
trait OneOffCookieSessionEndpoints
43+
case class OneOffCookieSessionEndpoints(system: ActorSystem[_], checkHeaderAndForm: Boolean)
4244
extends SessionEndpoints
43-
with GenericOneOffCookieSessionEndpoints[Session] {
44-
_: CsrfCheck =>
45-
}
45+
with GenericOneOffCookieSessionEndpoints[Session]
4646

47-
trait OneOffHeaderSessionEndpoints
47+
case class OneOffHeaderSessionEndpoints(system: ActorSystem[_], checkHeaderAndForm: Boolean)
4848
extends SessionEndpoints
49-
with GenericOneOffHeaderSessionEndpoints[Session] {
50-
_: CsrfCheck =>
51-
}
49+
with GenericOneOffHeaderSessionEndpoints[Session]
5250

53-
trait RefreshableCookieSessionEndpoints
51+
case class RefreshableCookieSessionEndpoints(system: ActorSystem[_], checkHeaderAndForm: Boolean)
5452
extends SessionEndpoints
55-
with GenericRefreshableCookieSessionEndpoints[Session] {
56-
_: CsrfCheck =>
57-
}
53+
with GenericRefreshableCookieSessionEndpoints[Session]
5854

59-
trait RefreshableHeaderSessionEndpoints
55+
case class RefreshableHeaderSessionEndpoints(system: ActorSystem[_], checkHeaderAndForm: Boolean)
6056
extends SessionEndpoints
61-
with GenericRefreshableHeaderSessionEndpoints[Session] {
62-
_: CsrfCheck =>
57+
with GenericRefreshableHeaderSessionEndpoints[Session]
58+
59+
object SessionEndpoints {
60+
def oneOffCookie(implicit
61+
system: ActorSystem[_],
62+
checkHeaderAndForm: Boolean = false
63+
): SessionEndpoints = OneOffCookieSessionEndpoints(system, checkHeaderAndForm)
64+
65+
def oneOffHeader(implicit
66+
system: ActorSystem[_],
67+
checkHeaderAndForm: Boolean = false
68+
): SessionEndpoints = OneOffHeaderSessionEndpoints(system, checkHeaderAndForm)
69+
70+
def refreshableCookie(implicit
71+
system: ActorSystem[_],
72+
checkHeaderAndForm: Boolean = false
73+
): SessionEndpoints = RefreshableCookieSessionEndpoints(system, checkHeaderAndForm)
74+
75+
def refreshableHeader(implicit
76+
system: ActorSystem[_],
77+
checkHeaderAndForm: Boolean = false
78+
): SessionEndpoints = RefreshableHeaderSessionEndpoints(system, checkHeaderAndForm)
6379
}

session/core/src/main/scala/com/softwaremill/session/CsrfCheck.scala

Lines changed: 0 additions & 13 deletions
This file was deleted.

session/core/src/main/scala/com/softwaremill/session/CsrfEndpoints.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import sttp.tapir.{EndpointIO, _}
99

1010
import scala.concurrent.{ExecutionContext, Future}
1111

12-
trait CsrfEndpoints[T] { _: CsrfCheck =>
12+
trait CsrfEndpoints[T] extends CsrfCheck {
1313

1414
import com.softwaremill.session.AkkaToTapirImplicits._
1515

@@ -125,3 +125,15 @@ trait CsrfEndpoints[T] { _: CsrfCheck =>
125125
}
126126

127127
}
128+
129+
sealed trait CsrfCheck {
130+
def checkHeaderAndForm: Boolean
131+
}
132+
133+
trait CsrfCheckHeader extends CsrfCheck {
134+
val checkHeaderAndForm: Boolean = false
135+
}
136+
137+
trait CsrfCheckHeaderAndForm extends CsrfCheck {
138+
val checkHeaderAndForm: Boolean = true
139+
}

session/core/src/main/scala/com/softwaremill/session/GenericSessionEndpoints.scala

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import sttp.tapir.server.PartialServerEndpointWithSecurityOutput
77
import scala.concurrent.Future
88

99
trait GenericSessionEndpoints[T] extends CsrfEndpoints[T] {
10-
_: SessionTransportEndpoints[T] with SessionContinuityEndpoints[T] with CsrfCheck =>
10+
_: SessionTransportEndpoints[T] with SessionContinuityEndpoints[T] =>
1111

1212
final def antiCsrfWithRequiredSession: PartialServerEndpointWithSecurityOutput[
1313
(Seq[Option[String]], Method, Option[String], Option[String]),
@@ -63,40 +63,28 @@ trait GenericSessionEndpoints[T] extends CsrfEndpoints[T] {
6363

6464
}
6565

66-
trait GenericCookieSessionEndpoints[T] extends GenericSessionEndpoints[T] with CookieTransportEndpoints[T] {
67-
_: SessionContinuityEndpoints[T] with CsrfCheck =>
68-
}
69-
70-
trait GenericHeaderSessionEndpoints[T] extends GenericSessionEndpoints[T] with HeaderTransportEndpoints[T] {
71-
_: SessionContinuityEndpoints[T] with CsrfCheck =>
72-
}
73-
7466
trait GenericOneOffCookieSessionEndpoints[T]
75-
extends GenericCookieSessionEndpoints[T]
67+
extends GenericSessionEndpoints[T]
68+
with CookieTransportEndpoints[T]
7669
with OneOffSessionContinuity[T]
77-
with OneOffSessionEndpoints[T] {
78-
_: CsrfCheck =>
79-
}
70+
with OneOffSessionEndpoints[T]
8071

8172
trait GenericOneOffHeaderSessionEndpoints[T]
82-
extends GenericHeaderSessionEndpoints[T]
73+
extends GenericSessionEndpoints[T]
74+
with HeaderTransportEndpoints[T]
8375
with OneOffSessionContinuity[T]
84-
with OneOffSessionEndpoints[T] {
85-
_: CsrfCheck =>
86-
}
76+
with OneOffSessionEndpoints[T]
8777

8878
trait GenericRefreshableCookieSessionEndpoints[T]
89-
extends GenericCookieSessionEndpoints[T]
79+
extends GenericSessionEndpoints[T]
80+
with CookieTransportEndpoints[T]
9081
with RefreshableSessionContinuity[T]
9182
with RefreshableSessionEndpoints[T]
92-
with OneOffSessionEndpoints[T] {
93-
_: CsrfCheck =>
94-
}
83+
with OneOffSessionEndpoints[T]
9584

9685
trait GenericRefreshableHeaderSessionEndpoints[T]
97-
extends GenericHeaderSessionEndpoints[T]
86+
extends GenericSessionEndpoints[T]
87+
with HeaderTransportEndpoints[T]
9888
with RefreshableSessionContinuity[T]
9989
with RefreshableSessionEndpoints[T]
100-
with OneOffSessionEndpoints[T] {
101-
_: CsrfCheck =>
102-
}
90+
with OneOffSessionEndpoints[T]

session/core/src/main/scala/com/softwaremill/session/RefreshableSessionEndpoints.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ trait RefreshableSessionEndpoints[T] extends Completion {
4242
}
4343

4444
def sendRefreshTokenToClientAsHeader: EndpointIO.Header[Option[String]] = {
45-
header[Option[String]](manager.config.sessionHeaderConfig.sendToClientHeaderName)
45+
header[Option[String]](manager.config.refreshTokenHeaderConfig.sendToClientHeaderName)
4646
}
4747

4848
private[session] def rotateToken(
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package app.softnetwork.session.scalatest
2+
3+
import com.softwaremill.session.CookieConfig
4+
import org.scalatest.Suite
5+
6+
trait CookieSessionTestKit extends SessionTestKit {
7+
_: Suite =>
8+
9+
def cookieConfig: CookieConfig
10+
11+
final override val sessionHeaderName: String = cookieConfig.name
12+
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package app.softnetwork.session.scalatest
2+
3+
import akka.http.scaladsl.model.headers.RawHeader
4+
import com.softwaremill.session.HeaderConfig
5+
import org.scalatest.Suite
6+
7+
trait HeaderSessionTestKit extends SessionTestKit {
8+
_: Suite =>
9+
10+
def headerConfig: HeaderConfig
11+
12+
final override val sessionHeaderName: String = headerConfig.sendToClientHeaderName
13+
14+
final override def mapRawHeader: RawHeader => Option[RawHeader] = raw =>
15+
if (raw.name == sessionHeaderName)
16+
Some(RawHeader(headerConfig.getFromClientHeaderName, raw.value))
17+
else
18+
Some(raw)
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package app.softnetwork.session.scalatest
2+
3+
import akka.actor.typed.ActorSystem
4+
import app.softnetwork.session.service.SessionEndpoints
5+
import com.softwaremill.session.{CookieConfig, CsrfCheck}
6+
import org.scalatest.Suite
7+
8+
trait OneOffCookieSessionTestKit extends CookieSessionTestKit with OneOffSessionTestKit {
9+
_: Suite with CsrfCheck =>
10+
11+
override def cookieConfig: CookieConfig = sessionManager.config.sessionCookieConfig
12+
13+
override def sessionEndpoints: ActorSystem[_] => SessionEndpoints = system =>
14+
SessionEndpoints.oneOffCookie(system, checkHeaderAndForm)
15+
}

0 commit comments

Comments
 (0)