From b3ae9531b244afb6a9c9bf00d3079497725cc89a Mon Sep 17 00:00:00 2001 From: niladic Date: Tue, 29 Oct 2024 14:04:02 +0100 Subject: [PATCH] =?UTF-8?q?Ajoute=20le=20user-agent=20=C3=A0=20la=20sessio?= =?UTF-8?q?n=20(#2114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/actions/LoginAction.scala | 3 +++ app/controllers/LoginController.scala | 3 ++- app/controllers/SignupController.scala | 2 ++ app/models/UserSession.scala | 1 + app/services/UserService.scala | 20 ++++++++++++++------ conf/evolutions/default/77.sql | 8 ++++++++ 6 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 conf/evolutions/default/77.sql diff --git a/app/actions/LoginAction.scala b/app/actions/LoginAction.scala index 71394f2f..183902d6 100644 --- a/app/actions/LoginAction.scala +++ b/app/actions/LoginAction.scala @@ -14,6 +14,7 @@ import models.{Area, Authorization, Error, EventType, LoginToken, User, UserSess import models.EventType.{AuthWithDifferentIp, ExpiredToken, ToCGURedirected, TryLoginByKey} import modules.AppConfig import play.api.Logger +import play.api.http.HeaderNames.USER_AGENT import play.api.mvc._ import play.api.mvc.Results.{InternalServerError, TemporaryRedirect} import scala.concurrent.{ExecutionContext, Future} @@ -228,6 +229,7 @@ class BaseLoginAction( UserSession.LoginType.InsecureDemoKey, loginExpiresAt, request.remoteAddress, + request.headers.get(USER_AGENT), ) _ <- EitherT .right[Error](IO.blocking(userService.recordLogin(user.id))) @@ -399,6 +401,7 @@ class BaseLoginAction( UserSession.LoginType.MagicLink, loginExpiresAt, request.remoteAddress, + request.headers.get(USER_AGENT), ) _ <- EitherT .right[Error](IO.blocking(userService.recordLogin(user.id))) diff --git a/app/controllers/LoginController.scala b/app/controllers/LoginController.scala index 33c478f4..9c86afe6 100644 --- a/app/controllers/LoginController.scala +++ b/app/controllers/LoginController.scala @@ -300,7 +300,8 @@ class LoginController @Inject() ( user.id, UserSession.LoginType.Password, expiresAt, - request.remoteAddress + request.remoteAddress, + request.headers.get(USER_AGENT), ) _ <- EitherT.right[Error]( IO.blocking( diff --git a/app/controllers/SignupController.scala b/app/controllers/SignupController.scala index f708dcab..bff85e97 100644 --- a/app/controllers/SignupController.scala +++ b/app/controllers/SignupController.scala @@ -143,6 +143,7 @@ case class SignupController @Inject() ( UserSession.LoginType.MagicLink, loginExpiresAt, request.remoteAddress, + request.headers.get(USER_AGENT), ) _ <- EitherT .right[Error](IO.blocking(userService.recordLogin(user.id))) @@ -398,6 +399,7 @@ case class SignupController @Inject() ( UserSession.LoginType.MagicLink, loginExpiresAt, request.remoteAddress, + request.headers.get(USER_AGENT), ) _ <- EitherT .right[Error](IO.blocking(userService.recordLogin(existingUser.id))) diff --git a/app/models/UserSession.scala b/app/models/UserSession.scala index dc396b76..32ee5466 100644 --- a/app/models/UserSession.scala +++ b/app/models/UserSession.scala @@ -24,6 +24,7 @@ case class UserSession( loginType: UserSession.LoginType, expiresAt: Instant, revokedAt: Option[Instant], + userAgent: Option[String], ) { def isValid(now: Instant): Boolean = diff --git a/app/services/UserService.scala b/app/services/UserService.scala index 9e204e20..d5324da4 100644 --- a/app/services/UserService.scala +++ b/app/services/UserService.scala @@ -679,6 +679,7 @@ class UserService @Inject() ( "login_type", "expires_at", "revoked_at", + "user_agent", ) private val qualifiedUserSessionParser = anorm.Macro.parser[UserSession]( @@ -690,6 +691,7 @@ class UserService @Inject() ( "user_session.login_type", "user_session.expires_at", "user_session.revoked_at", + "user_session.user_agent", ) // Double the recommended minimum 64 bits of entropy @@ -705,7 +707,8 @@ class UserService @Inject() ( userId: UUID, loginType: UserSession.LoginType, expiresAt: Instant, - ipAddress: String + ipAddress: String, + userAgent: Option[String], ): IO[Either[Error, UserSession]] = generateNewSessionId .flatMap(sessionId => @@ -718,7 +721,8 @@ class UserService @Inject() ( lastActivity = now, loginType = loginType, expiresAt = expiresAt, - revokedAt = None, + revokedAt = none, + userAgent = userAgent, ) ) ) @@ -743,6 +747,7 @@ class UserService @Inject() ( private def saveUserSession(session: UserSession): IO[Either[Error, UserSession]] = IO.blocking { val _ = db.withConnection { implicit connection => + val userAgent = session.userAgent.map(_.take(2048)) SQL""" INSERT INTO user_session ( id, @@ -751,7 +756,8 @@ class UserService @Inject() ( creation_ip_address, last_activity, login_type, - expires_at + expires_at, + user_agent ) VALUES ( ${session.id}, ${session.userId}::uuid, @@ -759,7 +765,8 @@ class UserService @Inject() ( ${session.creationIpAddress}::inet, ${session.lastActivity}, ${stringifyLoginType(session.loginType)}, - ${session.expiresAt} + ${session.expiresAt}, + $userAgent ) """.executeUpdate() } @@ -786,10 +793,11 @@ class UserService @Inject() ( userId: UUID, loginType: UserSession.LoginType, expiresAt: Instant, - ipAddress: String + ipAddress: String, + userAgent: Option[String], ): EitherT[IO, Error, UserSession] = for { - session <- EitherT(generateNewUserSession(userId, loginType, expiresAt, ipAddress)) + session <- EitherT(generateNewUserSession(userId, loginType, expiresAt, ipAddress, userAgent)) _ <- EitherT(saveUserSession(session)) } yield session diff --git a/conf/evolutions/default/77.sql b/conf/evolutions/default/77.sql new file mode 100644 index 00000000..d78180a1 --- /dev/null +++ b/conf/evolutions/default/77.sql @@ -0,0 +1,8 @@ +-- !Ups + +ALTER TABLE user_session ADD user_agent varchar(2048); + + +-- !Downs + +ALTER TABLE user_session DROP user_agent;