Skip to content

Commit 7ced522

Browse files
committed
upgrade to softwaremill PR #86 (softwaremill/akka-http-session#86)
1 parent 27d024c commit 7ced522

File tree

11 files changed

+296
-239
lines changed

11 files changed

+296
-239
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ ThisBuild / organization := "app.softnetwork"
3030

3131
name := "generic-persistence-api"
3232

33-
ThisBuild / version := "0.3.2"
33+
ThisBuild / version := "0.3.2.1"
3434

3535
ThisBuild / scalaVersion := "2.12.11"
3636

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.softwaremill.session
2+
3+
import scala.concurrent.{Await, Future}
4+
import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration}
5+
import scala.util.{Failure, Success, Try}
6+
7+
import scala.language.implicitConversions
8+
9+
trait Completion {
10+
11+
/** maximum wait time, which may be negative (no waiting is done),
12+
* [[scala.concurrent.duration.Duration.Inf Duration.Inf]] for unbounded waiting, or a finite
13+
* positive duration
14+
*/
15+
def defaultTimeout: FiniteDuration = 60.seconds
16+
17+
implicit class AwaitCompletion[T](future: Future[T])(implicit atMost: Duration = defaultTimeout) {
18+
19+
/** Usage: aFuture wait { case a: A => //... case other => //... } match { case Success(s) => s
20+
* case Failure(f) => //... }
21+
*
22+
* @param fun
23+
* - the function which will be called
24+
* @tparam B
25+
* - the function return type
26+
* @return
27+
* a successfull B or an exception
28+
*/
29+
def await[B](fun: T => B): Try[B] =
30+
Try(Await.result(future, atMost)) match {
31+
case Success(s) => Success(fun(s))
32+
case Failure(f) => Failure(f)
33+
}
34+
def complete(): Try[T] = await[T] { t =>
35+
t
36+
}
37+
}
38+
39+
}
40+
41+
object Completion extends Completion

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

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,13 @@
11
package com.softwaremill.session
22

33
import sttp.model.Method
4-
import sttp.model.headers.{CookieValueWithMeta, CookieWithMeta}
5-
import sttp.tapir._
4+
import sttp.model.headers.CookieValueWithMeta
65
import sttp.tapir.server.PartialServerEndpointWithSecurityOutput
76

87
import scala.concurrent.{ExecutionContext, Future}
98

109
trait CsrfEndpoints {
1110

12-
/** Protects against CSRF attacks using a double-submit cookie. The cookie will be set on any
13-
* `GET` request which doesn't have the token set in the header. For all other requests, the
14-
* value of the token from the CSRF cookie must match the value in the custom header (or request
15-
* body, if `checkFormBody` is `true`).
16-
*
17-
* The cookie value is the concatenation of a timestamp and its HMAC hash following the OWASP
18-
* recommendation for CSRF prevention:
19-
* @see
20-
* <a
21-
* href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#hmac-based-token-pattern">OWASP</a>
22-
*
23-
* Note that this scheme can be broken when not all subdomains are protected or not using HTTPS
24-
* and secure cookies, and the token is placed in the request body (not in the header).
25-
*
26-
* See the documentation for more details.
27-
*/
2811
def hmacTokenCsrfProtection[T, SECURITY_INPUT, PRINCIPAL, SECURITY_OUTPUT](
2912
checkMode: TapirCsrfCheckMode[T]
3013
)(
@@ -52,19 +35,11 @@ trait CsrfEndpoints {
5235
body
5336
}
5437

55-
def csrfCookie[T](
56-
checkMode: TapirCsrfCheckMode[T]
57-
): EndpointIO.Header[Option[CookieValueWithMeta]] =
58-
checkMode.csrfCookie
59-
60-
def csrfTokenFromCookie[T](
61-
checkMode: TapirCsrfCheckMode[T]
62-
): Endpoint[Option[String], Unit, Unit, Unit, Any] =
63-
checkMode.csrfTokenFromCookie()
64-
6538
def setNewCsrfToken[T](
6639
checkMode: TapirCsrfCheckMode[T]
67-
): CookieWithMeta =
40+
): PartialServerEndpointWithSecurityOutput[Unit, Unit, Unit, Unit, Option[
41+
CookieValueWithMeta
42+
], Unit, Any, Future] =
6843
checkMode.setNewCsrfToken()
6944
}
7045

@@ -77,7 +52,7 @@ object TapirCsrfOptions {
7752
new TapirCsrfCheckHeaderAndForm[T]()
7853
}
7954

80-
sealed trait TapirCsrfCheckMode[T] extends TapirCsrf[T] {
55+
sealed trait TapirCsrfCheckMode[T] extends TapirCsrf[T] { _: CsrfCheck =>
8156
def manager: SessionManager[T]
8257
def ec: ExecutionContext
8358
def csrfManager: CsrfManager[T] = manager.csrfManager

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

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,6 @@ private[session] trait OneOffTapirSession[T] {
119119
case CookieOrHeaderST => oneOffCookieOrHeaderSession(required)
120120
}
121121

122-
private[this] def oneOffCookieSessionLogic(
123-
cookie: Option[String],
124-
required: Option[Boolean]
125-
): Either[Unit, (Option[CookieValueWithMeta], SessionResult[T])] = {
126-
oneOffCookieOrHeaderSessionLogic(cookie, None, required).map(e => (e._1._1, e._2))
127-
}
128-
129122
def oneOffCookieSession(
130123
required: Option[Boolean] = None
131124
): PartialServerEndpointWithSecurityOutput[Seq[Option[String]], SessionResult[
@@ -140,23 +133,19 @@ private[session] trait OneOffTapirSession[T] {
140133
)
141134
.errorOut(statusCode(StatusCode.Unauthorized))
142135
.serverSecurityLogicWithOutput { inputs =>
143-
Future.successful(oneOffCookieSessionLogic(inputs.head, required) match {
144-
case Left(l) => Left(l)
145-
case Right(r) => Right(Seq(r._1.map(_.value)), r._2)
146-
})
136+
Future.successful(
137+
oneOffCookieOrHeaderSessionLogic(inputs.head, None, required)
138+
.map(e => (e._1._1, e._2)) match {
139+
case Left(l) => Left(l)
140+
case Right(r) => Right((Seq(r._1), r._2))
141+
}
142+
)
147143
}
148144
}
149145

150146
def extractOneOffSession(maybeValue: Option[String]): Option[T] =
151147
maybeValue.flatMap(manager.clientSessionManager.decode(_).toOption)
152148

153-
private[this] def oneOffHeaderSessionLogic(
154-
header: Option[String],
155-
required: Option[Boolean]
156-
): Either[Unit, (Option[String], SessionResult[T])] = {
157-
oneOffCookieOrHeaderSessionLogic(None, header, required).map(e => (e._1._2, e._2))
158-
}
159-
160149
def oneOffHeaderSession(
161150
required: Option[Boolean] = None
162151
): PartialServerEndpointWithSecurityOutput[Seq[Option[String]], SessionResult[
@@ -171,49 +160,52 @@ private[session] trait OneOffTapirSession[T] {
171160
.mapOut(Seq(_))(_.head)
172161
.errorOut(statusCode(StatusCode.Unauthorized))
173162
.serverSecurityLogicWithOutput { inputs =>
174-
Future.successful(oneOffHeaderSessionLogic(inputs.head, required) match {
175-
case Left(l) => Left(l)
176-
case Right(r) => Right(Seq(r._1), r._2)
177-
})
163+
Future.successful(
164+
oneOffCookieOrHeaderSessionLogic(None, inputs.head, required)
165+
.map(e => (e._1._2, e._2)) match {
166+
case Left(l) => Left(l)
167+
case Right(r) => Right((Seq(r._1), r._2))
168+
}
169+
)
178170
}
179171

180172
def oneOffCookieOrHeaderSessionLogic(
181173
maybeCookie: Option[String],
182174
maybeHeader: Option[String],
183175
required: Option[Boolean]
184-
): Either[Unit, ((Option[CookieValueWithMeta], Option[String]), SessionResult[T])] = {
185-
maybeCookie match {
186-
case Some(cookie) =>
187-
val decoded = manager.clientSessionManager.decode(cookie)
176+
): Either[Unit, ((Option[String], Option[String]), SessionResult[T])] = {
177+
// read session from the cookie and/or header
178+
val oneOff = maybeCookie.fold(maybeHeader)(Some(_))
179+
oneOff match {
180+
case Some(value) =>
181+
val decoded = manager.clientSessionManager.decode(value)
188182
decoded match {
189-
case SessionResult.Expired | SessionResult.Corrupt(_) =>
190-
Right((None, maybeHeader), decoded)
183+
case s: SessionResult.DecodedLegacy[T] =>
184+
Right(
185+
(
186+
(
187+
Some(manager.clientSessionManager.encode(s.session)),
188+
maybeHeader.map(_ => value)
189+
),
190+
s
191+
)
192+
)
191193
case s =>
192194
Right(
193195
(
194-
Some(manager.clientSessionManager.createCookieWithValue(cookie).valueWithMeta),
195-
maybeHeader
196-
),
197-
s
196+
(
197+
None,
198+
None
199+
),
200+
s
201+
)
198202
)
199203
}
200204
case _ =>
201-
maybeHeader match {
202-
case Some(header) =>
203-
val decoded = manager.clientSessionManager.decode(header)
204-
decoded match {
205-
case SessionResult.Expired | SessionResult.Corrupt(_) =>
206-
if (required.getOrElse(false))
207-
Left(())
208-
else
209-
Right((None, None), decoded)
210-
case s => Right((None, Some(header)), s)
211-
}
212-
case _ =>
213-
if (required.getOrElse(false))
214-
Left(())
215-
else
216-
Right((None, None), SessionResult.NoSession)
205+
if (required.getOrElse(false)) {
206+
Left(())
207+
} else {
208+
Right(((None, None), SessionResult.NoSession))
217209
}
218210
}
219211
}
@@ -239,7 +231,7 @@ private[session] trait OneOffTapirSession[T] {
239231
.serverSecurityLogicWithOutput { inputs =>
240232
Future.successful(
241233
oneOffCookieOrHeaderSessionLogic(inputs.head, inputs.last, required)
242-
.map(result => (Seq(result._1._1.map(_.value), result._1._2), result._2))
234+
.map(result => (Seq(result._1._1, result._1._2), result._2))
243235
)
244236
}
245237

@@ -254,33 +246,37 @@ private[session] trait OneOffTapirSession[T] {
254246
maybeHeader match {
255247
case Some(_) =>
256248
Right(
257-
Seq(
258-
Some("deleted"),
259-
Some("")
260-
),
261-
principal
249+
(
250+
Seq(
251+
Some("deleted"),
252+
Some("")
253+
),
254+
principal
255+
)
262256
)
263257
case _ =>
264258
Right(
265-
Seq(
266-
Some("deleted"),
267-
None
268-
),
269-
principal
259+
(
260+
Seq(
261+
Some("deleted"),
262+
None
263+
),
264+
principal
265+
)
270266
)
271267
}
272268
case _ =>
273269
maybeHeader match {
274-
case Some(_) => Right(Seq(None, Some("")), principal)
275-
case _ => Right(Seq(None, None), principal)
270+
case Some(_) => Right((Seq(None, Some("")), principal))
271+
case _ => Right((Seq(None, None), principal))
276272
}
277273
}
278274
}
279275

280276
def invalidateOneOffSession[
281277
SECURITY_INPUT,
282278
PRINCIPAL
283-
](
279+
](st: GetSessionTransport)(
284280
body: => PartialServerEndpointWithSecurityOutput[
285281
SECURITY_INPUT,
286282
PRINCIPAL,
@@ -343,12 +339,12 @@ private[session] trait OneOffTapirSession[T] {
343339
val session = r._2
344340
st match {
345341
case CookieST | HeaderST =>
346-
setOneOffSessionLogic(session.toOption, inputs.head)
342+
setOneOffSessionLogic(session.toOption, None)
347343
.map(result => (Seq(result), session))
348344
case CookieOrHeaderST =>
349345
val maybeCookie = inputs.head
350346
val maybeHeader = inputs.last
351-
setOneOffSessionLogic(session.toOption, inputs.head)
347+
setOneOffSessionLogic(session.toOption, None)
352348
.map(result =>
353349
(
354350
Seq(maybeCookie.flatMap(_ => result), maybeHeader.flatMap(_ => result)),

0 commit comments

Comments
 (0)