diff --git a/app/controllers/AccountController.scala b/app/controllers/AccountController.scala index 48223098..f87092f7 100644 --- a/app/controllers/AccountController.scala +++ b/app/controllers/AccountController.scala @@ -158,10 +158,14 @@ class AccountController( } yield NoContent } - def updateEmailAddress(token: String) = SecuredAction.async(parse.json) { implicit request => + def updateEmailAddress(token: String) = SecuredAction.async { implicit request => for { updatedUser <- accessesOrchestrator.updateEmailAddress(request.identity, token) - } yield Ok(Json.toJson(updatedUser)) + cookie <- authenticator.init(updatedUser.email) match { + case Right(value) => Future.successful(value) + case Left(error) => Future.failed(error) + } + } yield authenticator.embed(cookie, Ok(Json.toJson(updatedUser))) } def softDelete(id: UUID) = diff --git a/app/orchestrators/AccessesOrchestrator.scala b/app/orchestrators/AccessesOrchestrator.scala index cd0b2853..d8a70308 100644 --- a/app/orchestrators/AccessesOrchestrator.scala +++ b/app/orchestrators/AccessesOrchestrator.scala @@ -59,6 +59,12 @@ class AccessesOrchestrator( emailedTo <- updateEmailToken.emailedTo.liftTo[Future]( ServerError(s"Email should be defined for access token $token") ) + _ <- user.userRole match { + case UserRole.DGAL | UserRole.DGCCRF => + accessTokenRepository.validateEmail(updateEmailToken, user) + case UserRole.Admin | UserRole.Professionnel => + accessTokenRepository.invalidateToken(updateEmailToken) + } updatedUser <- if (isSameUser) userOrchestrator.updateEmail(user, emailedTo) else Future.failed(DifferentUserFromRequest(user.id, updateEmailToken.userId)) @@ -79,15 +85,17 @@ class AccessesOrchestrator( _ <- if (emailValidationFunction(newEmail.value)) Future.unit else Future.failed(InvalidDGCCRFOrAdminEmail(List(newEmail))) - existingTokens <- accessTokenRepository.fetchPendingTokens(newEmail) - existingToken = existingTokens.find(_.kind == UpdateEmail) + existingTokens <- accessTokenRepository.fetchPendingTokens(user) + existingToken = existingTokens.headOption token <- existingToken match { case Some(token) => - logger.debug("reseting token validity") + logger.debug("reseting token validity and email") accessTokenRepository.update( token.id, - AccessToken.resetExpirationDate(token, tokenConfiguration.updateEmailAddress) + AccessToken + .resetExpirationDate(token, tokenConfiguration.updateEmailAddress) + .copy(emailedTo = Some(newEmail)) ) case None => logger.debug("creating token") diff --git a/app/repositories/accesstoken/AccessTokenRepository.scala b/app/repositories/accesstoken/AccessTokenRepository.scala index 11e7372a..20beee97 100644 --- a/app/repositories/accesstoken/AccessTokenRepository.scala +++ b/app/repositories/accesstoken/AccessTokenRepository.scala @@ -9,6 +9,7 @@ import models.token.TokenKind.CompanyFollowUp import models.token.TokenKind.CompanyInit import models.token.TokenKind.DGALAccount import models.token.TokenKind.DGCCRFAccount +import models.token.TokenKind.UpdateEmail import repositories.accesstoken.AccessTokenColumnType._ import repositories.company.CompanyTable import repositories.companyaccess.CompanyAccessColumnType._ @@ -123,6 +124,14 @@ class AccessTokenRepository( fetchCompanyValidTokens(company).delete ) + override def fetchPendingTokens(user: User): Future[List[AccessToken]] = db.run( + fetchValidTokens + .filter(_.userId === user.id) + .filter(_.kind === (UpdateEmail: TokenKind)) + .to[List] + .result + ) + override def fetchPendingTokens(emailedTo: EmailAddress): Future[List[AccessToken]] = db.run( table diff --git a/app/repositories/accesstoken/AccessTokenRepositoryInterface.scala b/app/repositories/accesstoken/AccessTokenRepositoryInterface.scala index 825359be..aebbaa52 100644 --- a/app/repositories/accesstoken/AccessTokenRepositoryInterface.scala +++ b/app/repositories/accesstoken/AccessTokenRepositoryInterface.scala @@ -28,6 +28,8 @@ trait AccessTokenRepositoryInterface extends CRUDRepositoryInterface[AccessToken def findValidToken(company: Company, token: String): Future[Option[AccessToken]] + def fetchPendingTokens(user: User): Future[List[AccessToken]] + def fetchPendingTokens(company: Company): Future[List[AccessToken]] def removePendingTokens(company: Company): Future[Int] diff --git a/app/utils/FrontRoute.scala b/app/utils/FrontRoute.scala index 53310ae0..018edf8b 100644 --- a/app/utils/FrontRoute.scala +++ b/app/utils/FrontRoute.scala @@ -29,7 +29,7 @@ class FrontRoute(signalConsoConfiguration: SignalConsoConfiguration) { def resetPassword(authToken: AuthToken) = url(s"/connexion/nouveau-mot-de-passe/${authToken.id}") def activation = url("/activation") def welcome = url("/") - def updateEmail(token: String) = url(s"/update-email/$token") + def updateEmail(token: String) = url(s"/parametres/update-email/$token") object Admin { def register(token: String) = url(s"/admin/rejoindre/?token=$token") diff --git a/conf/common/app.conf b/conf/common/app.conf index d023a71c..9035b426 100644 --- a/conf/common/app.conf +++ b/conf/common/app.conf @@ -29,6 +29,7 @@ app { dgccrf-join-duration = "P60D" dgccrf-delay-before-revalidation = "P90D" dgccrf-revalidation-token-duration = "P7D" + update-email-address = "P2D" } mobile-app { diff --git a/conf/db/migration/default/V18__add_user_id_to_access_tokens.sql b/conf/db/migration/default/V18__add_user_id_to_access_tokens.sql new file mode 100644 index 00000000..5e7136f6 --- /dev/null +++ b/conf/db/migration/default/V18__add_user_id_to_access_tokens.sql @@ -0,0 +1,3 @@ +ALTER TABLE access_tokens + ADD user_id UUID, + ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id); \ No newline at end of file