diff --git a/app/controllers/ApplicationController.scala b/app/controllers/ApplicationController.scala index 4a4d2c3a4..64afdf876 100644 --- a/app/controllers/ApplicationController.scala +++ b/app/controllers/ApplicationController.scala @@ -16,6 +16,7 @@ import models.EventType._ import models._ import models.formModels.{AnswerFormData, ApplicationFormData, InvitationFormData} import models.mandat.Mandat +import modules.AppConfig import org.webjars.play.WebJarsUtil import play.api.cache.AsyncCacheApi import play.api.data.Forms._ @@ -46,7 +47,7 @@ import scala.util.{Failure, Success, Try} case class ApplicationController @Inject() ( applicationService: ApplicationService, cache: AsyncCacheApi, - configuration: play.api.Configuration, + config: AppConfig, eventService: EventService, fileService: FileService, loginAction: LoginAction, @@ -63,25 +64,6 @@ case class ApplicationController @Inject() ( with Operators.ApplicationOperators with Operators.UserOperators { - private val filesPath = configuration.underlying.getString("app.filesPath") - private val featureMandatSms: Boolean = configuration.get[Boolean]("app.features.smsMandat") - - private val featureCanSendApplicationsAnywhere: Boolean = - configuration.get[Boolean]("app.features.canSendApplicationsAnywhere") - - // This is a feature that is temporary and should be activated - // for short period of time during migrations for smooth handling of files. - // Just remove the env variable FILES_SECOND_INSTANCE_HOST to deactivate. - private val filesSecondInstanceHost: Option[String] = - configuration.getOptional[String]("app.filesSecondInstanceHost") - - private val filesExpirationInDays: Int = configuration.get[Int]("app.filesExpirationInDays") - - private val dir = Paths.get(s"$filesPath") - if (!Files.isDirectory(dir)) { - Files.createDirectories(dir) - } - private val success = "success" private def filterVisibleGroups(areaId: UUID, user: User, rights: Authorization.UserRights)( @@ -176,8 +158,6 @@ case class ApplicationController @Inject() ( coworkers, readSharedAccountUserSignature(request.session), canCreatePhoneMandat = currentArea === Area.calvados, - featureMandatSms = featureMandatSms, - featureCanSendApplicationsAnywhere = featureCanSendApplicationsAnywhere, categories, ApplicationFormData.form(request.currentUser) ) @@ -268,8 +248,6 @@ case class ApplicationController @Inject() ( coworkers, None, canCreatePhoneMandat = currentArea === Area.calvados, - featureMandatSms = featureMandatSms, - featureCanSendApplicationsAnywhere = featureCanSendApplicationsAnywhere, organisationService.categories, form, Nil, @@ -296,8 +274,6 @@ case class ApplicationController @Inject() ( coworkers, None, canCreatePhoneMandat = currentArea === Area.calvados, - featureMandatSms = featureMandatSms, - featureCanSendApplicationsAnywhere = featureCanSendApplicationsAnywhere, organisationService.categories, formWithErrors, files, @@ -985,7 +961,6 @@ case class ApplicationController @Inject() ( selectedArea, readSharedAccountUserSignature(request.session), files, - fileExpiryDayCount = filesExpirationInDays ) ).withHeaders(CACHE_CONTROL -> "no-store") } @@ -1024,7 +999,10 @@ case class ApplicationController @Inject() ( withApplication(applicationId) { application: Application => val isAuthorized = Authorization - .fileCanBeShowed(filesExpirationInDays)(metadata.attached, application)( + .fileCanBeShowed(config.filesExpirationInDays)( + metadata.attached, + application + )( request.currentUser.id, request.rights ) @@ -1113,7 +1091,7 @@ case class ApplicationController @Inject() ( ).withHeaders(CACHE_CONTROL -> "no-store") ) } else { - filesSecondInstanceHost match { + config.filesSecondInstanceHost match { case None => eventService.log( FileNotFound, diff --git a/app/controllers/AreaController.scala b/app/controllers/AreaController.scala index edfc3f3b2..c1ddfd404 100644 --- a/app/controllers/AreaController.scala +++ b/app/controllers/AreaController.scala @@ -1,12 +1,9 @@ package controllers -import java.util.UUID - import actions.LoginAction import cats.syntax.all._ import constants.Constants import controllers.Operators.UserOperators -import helper.UUIDHelper import javax.inject.{Inject, Singleton} import models.EventType.{ AllAreaUnauthorized, @@ -15,31 +12,25 @@ import models.EventType.{ DeploymentDashboardUnauthorized } import models._ +import modules.AppConfig import org.webjars.play.WebJarsUtil import play.api.mvc.InjectedController +import scala.concurrent.{ExecutionContext, Future} import serializers.Keys import services.{EventService, UserGroupService, UserService} -import scala.concurrent.{ExecutionContext, Future} - @Singleton case class AreaController @Inject() ( + config: AppConfig, loginAction: LoginAction, eventService: EventService, userService: UserService, userGroupService: UserGroupService, - configuration: play.api.Configuration )(implicit ec: ExecutionContext, val webJarsUtil: WebJarsUtil) extends InjectedController with Operators.Common with UserOperators { - private lazy val areasWithLoginByKey: List[UUID] = configuration.underlying - .getString("app.areasWithLoginByKey") - .split(",") - .flatMap(UUIDHelper.fromString) - .toList - def all = loginAction.async { implicit request => if (!request.currentUser.admin && !request.currentUser.groupAdmin) { @@ -55,14 +46,7 @@ case class AreaController @Inject() ( Future(userGroupService.byIds(request.currentUser.groupIds)) } userGroupsFuture.map { userGroups => - Ok( - views.html - .allArea(request.currentUser, request.rights)( - Area.all, - areasWithLoginByKey, - userGroups - ) - ) + Ok(views.html.allArea(request.currentUser, request.rights)(Area.all, userGroups)) } } } diff --git a/app/controllers/CSVImportController.scala b/app/controllers/CSVImportController.scala index 3f25024c1..7d95376c6 100644 --- a/app/controllers/CSVImportController.scala +++ b/app/controllers/CSVImportController.scala @@ -31,8 +31,8 @@ import models.formModels.{ CSVUserGroupFormData } import models.{Area, Organisation, User, UserGroup} +import modules.AppConfig import org.webjars.play.WebJarsUtil -import play.api.Configuration import play.api.data.Forms._ import play.api.data.validation.Constraints.{maxLength, nonEmpty} import play.api.data.{Form, Mapping} @@ -44,7 +44,7 @@ import services.{EventService, NotificationService, UserGroupService, UserServic import scala.concurrent.{ExecutionContext, Future} case class CSVImportController @Inject() ( - val configuration: Configuration, + config: AppConfig, loginAction: LoginAction, userService: UserService, groupService: UserGroupService, diff --git a/app/controllers/GroupController.scala b/app/controllers/GroupController.scala index 2d0cef259..e3d718e2e 100644 --- a/app/controllers/GroupController.scala +++ b/app/controllers/GroupController.scala @@ -12,7 +12,6 @@ import javax.inject.{Inject, Singleton} import models.{Area, Authorization, Error, EventType, Organisation, User, UserGroup} import models.formModels.{normalizedOptionalText, normalizedText, AddUserToGroupFormData} import org.webjars.play.WebJarsUtil -import play.api.Configuration import play.api.data.Form import play.api.data.Forms.{email, ignored, list, mapping, of, optional, text, uuid} import play.api.data.validation.Constraints.maxLength @@ -36,16 +35,17 @@ import models.EventType.{ UserGroupDeletionUnauthorized, UserGroupEdited } +import modules.AppConfig import scala.concurrent.{ExecutionContext, Future} import serializers.Keys @Singleton case class GroupController @Inject() ( + config: AppConfig, applicationService: ApplicationService, loginAction: LoginAction, groupService: UserGroupService, eventService: EventService, - configuration: Configuration, ws: WSClient, userService: UserService )(implicit ec: ExecutionContext, webJarsUtil: WebJarsUtil) diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala index d62496996..485afda13 100644 --- a/app/controllers/HomeController.scala +++ b/app/controllers/HomeController.scala @@ -3,7 +3,8 @@ package controllers import javax.inject.{Inject, Singleton} import actions.LoginAction import org.webjars.play.WebJarsUtil -import play.api.{Configuration, Logger} +import modules.AppConfig +import play.api.Logger import play.api.mvc._ import play.api.db.Database import serializers.Keys @@ -13,7 +14,7 @@ import views.home.LoginPanel */ @Singleton class HomeController @Inject() ( - val configuration: Configuration, + val config: AppConfig, loginAction: LoginAction, db: Database )(implicit webJarsUtil: WebJarsUtil) diff --git a/app/controllers/LoginController.scala b/app/controllers/LoginController.scala index 1ec1f29fa..cd68fd9a9 100644 --- a/app/controllers/LoginController.scala +++ b/app/controllers/LoginController.scala @@ -7,8 +7,8 @@ import java.time.ZoneId import javax.inject.{Inject, Singleton} import models.EventType.{GenerateToken, UnknownEmail} import models.{Authorization, EventType, LoginToken, User} +import modules.AppConfig import org.webjars.play.WebJarsUtil -import play.api.Configuration import play.api.mvc.{Action, AnyContent, InjectedController, Request} import serializers.Keys import services.{EventService, NotificationService, SignupService, TokenService, UserService} @@ -17,19 +17,16 @@ import scala.concurrent.{ExecutionContext, Future} @Singleton class LoginController @Inject() ( + val config: AppConfig, userService: UserService, notificationService: NotificationService, tokenService: TokenService, - val configuration: Configuration, eventService: EventService, signupService: SignupService )(implicit ec: ExecutionContext, webJarsUtil: WebJarsUtil) extends InjectedController with Operators.Common { - private lazy val tokenExpirationInMinutes = - configuration.get[Int]("app.tokenExpirationInMinutes") - /** Security Note: when the email is in the query "?email=xxx", we do not check the CSRF token * because the API is used externally. */ @@ -73,7 +70,11 @@ class LoginController @Inject() ( case Some(signup) => val loginToken = LoginToken - .forSignupId(signup.id, tokenExpirationInMinutes, request.remoteAddress) + .forSignupId( + signup.id, + config.tokenExpirationInMinutes, + request.remoteAddress + ) loginHappyPath(loginToken, signup.email, None) } ) @@ -81,7 +82,8 @@ class LoginController @Inject() ( } { user: User => LoginAction.readUserRights(user).map { userRights => val loginToken = - LoginToken.forUserId(user.id, tokenExpirationInMinutes, request.remoteAddress) + LoginToken + .forUserId(user.id, config.tokenExpirationInMinutes, request.remoteAddress) val requestWithUserData = new RequestWithUserData(user, userRights, request) loginHappyPath(loginToken, user.email, requestWithUserData.some) @@ -150,7 +152,6 @@ class LoginController @Inject() ( LoginPanel.EmailSentFeedback( email, requestWithUserData.map(_.currentUser.timeZone).getOrElse(Time.timeZoneParis), - tokenExpirationInMinutes, successMessage ) ) @@ -177,8 +178,7 @@ class LoginController @Inject() ( Ok( views.html.magicLinkAntiConsumptionPage( token = token, - pathToRedirectTo = path, - tokenExpirationInMinutes = tokenExpirationInMinutes + pathToRedirectTo = path ) ) case _ => diff --git a/app/controllers/MandatController.scala b/app/controllers/MandatController.scala index b8a2bfa91..90a6b3fb6 100644 --- a/app/controllers/MandatController.scala +++ b/app/controllers/MandatController.scala @@ -10,8 +10,8 @@ import controllers.Operators.UserOperators import javax.inject.{Inject, Singleton} import models.mandat.{Mandat, SmsMandatInitiation} import models.{Error, EventType, Sms} +import modules.AppConfig import org.webjars.play.WebJarsUtil -import play.api.Configuration import play.api.libs.json.{JsError, JsString, JsValue, Json} import play.api.mvc.{Action, AnyContent, InjectedController, PlayBodyParsers} import serializers.JsonFormats._ @@ -22,7 +22,7 @@ import scala.concurrent.{ExecutionContext, Future} @Singleton case class MandatController @Inject() ( bodyParsers: PlayBodyParsers, - val configuration: Configuration, + config: AppConfig, eventService: EventService, loginAction: LoginAction, mandatService: MandatService, diff --git a/app/controllers/Operators.scala b/app/controllers/Operators.scala index f4b2d4fa2..401fc8856 100644 --- a/app/controllers/Operators.scala +++ b/app/controllers/Operators.scala @@ -8,7 +8,7 @@ import constants.Constants import helper.BooleanHelper.not import models.EventType._ import models.{Application, Authorization, Error, EventType, User, UserGroup} -import play.api.Configuration +import modules.AppConfig import play.api.mvc.Results.{InternalServerError, NotFound, Unauthorized} import play.api.mvc.{AnyContent, RequestHeader, Result, Results} import scala.concurrent.{ExecutionContext, Future} @@ -18,14 +18,14 @@ import views.MainInfos object Operators { trait Common { - def configuration: Configuration + def config: AppConfig implicit def mainInfos(implicit request: RequestHeader): MainInfos = { val isDemo = request.domain.contains("localhost") || request.domain.contains("demo") MainInfos( isDemo = isDemo, - topHeaderWarningMessage = configuration.getOptional[String]("app.topHeaderWarningMessage") + config = config ) } diff --git a/app/controllers/SignupController.scala b/app/controllers/SignupController.scala index 76d371cf4..83e9079e4 100644 --- a/app/controllers/SignupController.scala +++ b/app/controllers/SignupController.scala @@ -13,8 +13,8 @@ import java.util.UUID import javax.inject.{Inject, Singleton} import models.{Authorization, Error, EventType, SignupRequest, User} import models.formModels.{AddSignupsFormData, SignupFormData} +import modules.AppConfig import org.webjars.play.WebJarsUtil -import play.api.Configuration import play.api.data.Form import play.api.i18n.I18nSupport import play.api.mvc.{Action, AnyContent, InjectedController, Request, Result} @@ -24,7 +24,7 @@ import services.{EventService, NotificationService, SignupService, UserGroupServ @Singleton case class SignupController @Inject() ( - configuration: Configuration, + config: AppConfig, eventService: EventService, groupService: UserGroupService, loginAction: LoginAction, diff --git a/app/controllers/UserController.scala b/app/controllers/UserController.scala index 4b149b6bd..3901604e6 100644 --- a/app/controllers/UserController.scala +++ b/app/controllers/UserController.scala @@ -51,9 +51,9 @@ import models.formModels.{ EditUserFormData, ValidateSubscriptionForm } +import modules.AppConfig import org.postgresql.util.PSQLException import org.webjars.play.WebJarsUtil -import play.api.Configuration import play.api.data.Forms._ import play.api.data.validation.Constraints.{maxLength, nonEmpty} import play.api.data.{Form, Mapping} @@ -70,12 +70,12 @@ import services._ @Singleton case class UserController @Inject() ( + config: AppConfig, loginAction: LoginAction, userService: UserService, groupService: UserGroupService, applicationService: ApplicationService, notificationsService: NotificationService, - configuration: Configuration, eventService: EventService )(implicit ec: ExecutionContext, webJarsUtil: WebJarsUtil) extends InjectedController diff --git a/app/modules/AppConfig.scala b/app/modules/AppConfig.scala new file mode 100644 index 000000000..89f09ef40 --- /dev/null +++ b/app/modules/AppConfig.scala @@ -0,0 +1,80 @@ +package modules + +import com.typesafe.config.{Config, ConfigFactory} +import helper.UUIDHelper +import java.nio.file.{Files, Path, Paths} +import java.util.UUID +import javax.inject.{Inject, Singleton} +import play.api.Configuration +import scala.concurrent.duration._ + +// Wraps `configuration: play.api.Configuration`. +// We want `configuration` to be private. +@Singleton +class AppConfig @Inject() (configuration: Configuration) { + + val tokenExpirationInMinutes: Int = + configuration.get[Int]("app.tokenExpirationInMinutes") + + val featureMandatSms: Boolean = configuration.get[Boolean]("app.features.smsMandat") + + val useLiveSmsApi: Boolean = configuration + .getOptional[Boolean]("app.smsUseLiveApi") + .getOrElse(false) + + val featureCanSendApplicationsAnywhere: Boolean = + configuration.get[Boolean]("app.features.canSendApplicationsAnywhere") + + val filesPath: String = configuration.get[String]("app.filesPath") + + val filesDirectory: Path = { + val dir = Paths.get(filesPath) + if (!Files.isDirectory(dir)) { + Files.createDirectories(dir) + } + dir + } + + // This is a feature that is temporary and should be activated + // for short period of time during migrations for smooth handling of files. + // Just remove the env variable FILES_SECOND_INSTANCE_HOST to deactivate. + val filesSecondInstanceHost: Option[String] = + configuration.getOptional[String]("app.filesSecondInstanceHost") + + val filesExpirationInDays: Int = configuration.get[Int]("app.filesExpirationInDays") + + val topHeaderWarningMessage: Option[String] = + configuration.getOptional[String]("app.topHeaderWarningMessage") + + val areasWithLoginByKey: List[UUID] = configuration + .get[String]("app.areasWithLoginByKey") + .split(",") + .flatMap(UUIDHelper.fromString) + .toList + + val clamAvIsEnabled: Boolean = configuration.get[Boolean]("app.clamav.enabled") + + val clamAvHost: String = configuration.get[String]("app.clamav.host") + val clamAvPort: Int = configuration.get[Int]("app.clamav.port") + val clamAvTimeout: FiniteDuration = configuration.get[Int]("app.clamav.timeoutInSeconds").seconds + + // This blacklist if mainly for experts who do not need emails + // Note: be careful with the empty string + val notificationEmailBlacklist: Set[String] = + configuration + .get[String]("app.notificationEmailBlacklist") + .split(",") + .map(_.trim) + .filterNot(_.isEmpty) + .toSet + + val defaultMailerConfig: Config = configuration.underlying.getObject("play.mailer").toConfig + + val emailPickersConfig: Option[Config] = + configuration + .getOptional[String]("app.mailer.pickersConfig") + .map { raw => + ConfigFactory.parseString(raw) + } + +} diff --git a/app/services/EmailsService.scala b/app/services/EmailsService.scala index 13a9ddbca..e25b56a42 100644 --- a/app/services/EmailsService.scala +++ b/app/services/EmailsService.scala @@ -7,7 +7,8 @@ import cats.syntax.all._ import helper.MiscHelpers import javax.inject.{Inject, Singleton} import models.EmailPriority -import play.api.{Configuration, Logger} +import modules.AppConfig +import play.api.Logger import play.api.libs.concurrent.MaterializerProvider import play.api.libs.mailer.{Email, SMTPConfiguration, SMTPMailer} import com.typesafe.config.{Config, ConfigFactory} @@ -54,7 +55,7 @@ object EmailsService { */ @Singleton class EmailsService @Inject() ( - configuration: Configuration, + config: AppConfig, dependencies: ServicesDependencies, materializerProvider: MaterializerProvider, ) { @@ -64,21 +65,10 @@ class EmailsService @Inject() ( private val log = Logger(classOf[EmailsService]) - // This blacklist if mainly for experts who do not need emails - // Note: be careful with the empty string - private lazy val notificationEmailBlacklist: Set[String] = - configuration - .get[String]("app.notificationEmailBlacklist") - .split(",") - .map(_.trim) - .filterNot(_.isEmpty) - .toSet - private def emailIsBlacklisted(email: Email): Boolean = - notificationEmailBlacklist.exists(black => email.to.exists(_.contains(black))) + config.notificationEmailBlacklist.exists(black => email.to.exists(_.contains(black))) - private val defaultMailerConfig = configuration.underlying.getObject("play.mailer").toConfig - private val defaultSMTPConfig = SMTPConfiguration(defaultMailerConfig) + private val defaultSMTPConfig = SMTPConfiguration(config.defaultMailerConfig) private val defaultSMTP = new SMTPMailer(defaultSMTPConfig) private val defaultHeaders = List[(String, String)]( @@ -88,17 +78,15 @@ class EmailsService @Inject() ( ) private val pickers: Option[SMTPPickers] = - configuration - .getOptional[String]("app.mailer.pickersConfig") - .map { raw => - val rootConfig = ConfigFactory.parseString(raw) + config.emailPickersConfig + .map { rootConfig => def readWeightedConfig(key: String) = SMTPPicker( NonEmptyList.fromListUnsafe(asScala(rootConfig.getObjectList(key)).toList).map { obj => val topConfig = obj.toConfig val weight = topConfig.getDouble("weight") val smtpConfig = SMTPConfiguration( - topConfig.getObject("smtpConfig").toConfig.withFallback(defaultMailerConfig) + topConfig.getObject("smtpConfig").toConfig.withFallback(config.defaultMailerConfig) ) val extraHeaders = Try(topConfig.getObject("extraHeaders")).toOption .map { obj => diff --git a/app/services/FileService.scala b/app/services/FileService.scala index d6641a492..5b49fd096 100644 --- a/app/services/FileService.scala +++ b/app/services/FileService.scala @@ -13,18 +13,17 @@ import java.util.UUID import javax.inject.{Inject, Singleton} import models.{Error, EventType, FileMetadata, User} import models.dataModels.FileMetadataRow +import modules.AppConfig import net.scalytica.clammyscan.streams.{ClamError, ClamIO, FileOk, VirusFound} -import play.api.Configuration import play.api.mvc.Request import play.api.libs.concurrent.{ActorSystemProvider, MaterializerProvider} import play.api.db.Database import scala.concurrent.{ExecutionContext, Future} -import scala.concurrent.duration._ import scala.util.{Success, Try} @Singleton class FileService @Inject() ( - configuration: Configuration, + config: AppConfig, db: Database, eventService: EventService, materializer: MaterializerProvider, @@ -33,21 +32,10 @@ class FileService @Inject() ( )(implicit ec: ExecutionContext) { implicit val actorSystem = system.get - val filesPath: String = { - val path = configuration.get[String]("app.filesPath") - val dir = Paths.get(path) - if (!Files.isDirectory(dir)) { - Files.createDirectories(dir) - } - path - } - - val clamAvIsEnabled = configuration.get[Boolean]("app.clamav.enabled") - val clamIo = ClamIO( - configuration.get[String]("app.clamav.host"), - configuration.get[Int]("app.clamav.port"), - configuration.get[Int]("app.clamav.timeoutInSeconds").seconds, + config.clamAvHost, + config.clamAvPort, + config.clamAvTimeout, // maxBytes = 10M 10000000 ) @@ -84,7 +72,9 @@ class FileService @Inject() ( } def fileMetadata(fileId: UUID): Future[Either[Error, Option[(Path, FileMetadata)]]] = - byId(fileId).map(_.map(_.map(metadata => (Paths.get(s"$filesPath/$fileId"), metadata)))) + byId(fileId).map( + _.map(_.map(metadata => (Paths.get(s"${config.filesPath}/$fileId"), metadata))) + ) private def scanFilesBackground(metadataList: List[(Path, FileMetadata)], uploader: User)(implicit request: Request[_] @@ -95,7 +85,7 @@ class FileService @Inject() ( .mapAsync(1) { case (path, metadata) => val sink = clamIo.scan(metadata.id.toString) val scanResult: Future[Either[Error, Unit]] = - if (clamAvIsEnabled) + if (config.clamAvIsEnabled) FileIO .fromPath(path) .toMat(sink)(Keep.right) @@ -107,7 +97,7 @@ class FileService @Inject() ( EventType.FileAvailable, s"Aucun virus détecté par ClamAV dans le fichier ${metadata.id}" ) - val fileDestination = Paths.get(s"$filesPath/${metadata.id}") + val fileDestination = Paths.get(s"${config.filesPath}/${metadata.id}") Files.copy(path, fileDestination) Files.deleteIfExists(path) FileMetadata.Status.Available @@ -136,7 +126,7 @@ class FileService @Inject() ( s"Le fichier ${metadata.id} est disponible. ClamAV est désactivé. " + "Aucun scan n'a été effectué" ) - val fileDestination = Paths.get(s"$filesPath/${metadata.id}") + val fileDestination = Paths.get(s"${config.filesPath}/${metadata.id}") Files.copy(path, fileDestination) Files.deleteIfExists(path) updateStatus(metadata.id, FileMetadata.Status.Available) @@ -286,7 +276,7 @@ class FileService @Inject() ( Source .fromIterator(() => files.iterator) .mapAsync(1) { metadata => - val path = Paths.get(s"$filesPath/${metadata.id}") + val path = Paths.get(s"${config.filesPath}/${metadata.id}") Files.deleteIfExists(path) updateStatus(metadata.id, FileMetadata.Status.Expired) .map(_.fold(e => eventService.logErrorNoRequest(e), identity)) diff --git a/app/services/SignupService.scala b/app/services/SignupService.scala index 3941b51be..d76f71899 100644 --- a/app/services/SignupService.scala +++ b/app/services/SignupService.scala @@ -13,7 +13,6 @@ import scala.util.Try @javax.inject.Singleton class SignupService @Inject() ( - configuration: play.api.Configuration, db: Database, dependencies: ServicesDependencies ) { diff --git a/app/services/SmsService.scala b/app/services/SmsService.scala index 6212352ee..2deb6b69c 100644 --- a/app/services/SmsService.scala +++ b/app/services/SmsService.scala @@ -5,6 +5,7 @@ import cats.syntax.all._ import javax.inject.{Inject, Singleton} import models.mandat.Mandat import models.{Error, EventType, Organisation, Sms, User, UserGroup} +import modules.AppConfig import play.api.Configuration import play.api.libs.concurrent.{Futures, MaterializerProvider} import play.api.libs.ws.WSClient @@ -15,6 +16,7 @@ import scala.concurrent.{ExecutionContext, Future} @Singleton class SmsService @Inject() ( bodyParsers: PlayBodyParsers, + config: AppConfig, configuration: Configuration, eventService: EventService, futures: Futures, @@ -22,12 +24,9 @@ class SmsService @Inject() ( ws: WSClient )(implicit ec: ExecutionContext) { - private val useLiveApi: Boolean = configuration - .getOptional[Boolean]("app.smsUseLiveApi") - .getOrElse(false) - private val api: SmsApi = - if (useLiveApi) new SmsApi.OvhSmsApi(bodyParsers, configuration, ws, ec, materializer.get) + if (config.useLiveSmsApi) + new SmsApi.OvhSmsApi(bodyParsers, configuration, ws, ec, materializer.get) else new SmsApi.FakeSmsApi(configuration, eventService, futures, ws, ec) def sendMandatSms( diff --git a/app/services/TokenService.scala b/app/services/TokenService.scala index 6ad07f7d4..6ea23c8ed 100644 --- a/app/services/TokenService.scala +++ b/app/services/TokenService.scala @@ -7,7 +7,7 @@ import java.time.Instant import java.util.UUID import javax.inject.Inject import models.{Error, EventType, LoginToken} -import play.api.{Configuration, UnexpectedException} +import play.api.UnexpectedException import play.api.db.Database import scala.concurrent.Future import scala.util.Try @@ -77,7 +77,6 @@ object TokenService { @javax.inject.Singleton class TokenService @Inject() ( - configuration: Configuration, db: Database, dependencies: ServicesDependencies ) { diff --git a/app/services/UserGroupService.scala b/app/services/UserGroupService.scala index b4fd1e09c..4a499fc26 100644 --- a/app/services/UserGroupService.scala +++ b/app/services/UserGroupService.scala @@ -10,14 +10,12 @@ import helper.{StringHelper, Time, UUIDHelper} import javax.inject.Inject import models.{Error, EventType, FranceService, Organisation, UserGroup} import org.postgresql.util.PSQLException -import play.api.Configuration import play.api.db.Database import scala.concurrent.Future import scala.util.Try @javax.inject.Singleton class UserGroupService @Inject() ( - configuration: Configuration, val db: Database, val dependencies: ServicesDependencies ) { diff --git a/app/views/MainInfos.scala b/app/views/MainInfos.scala index 742478290..bd9eb6c9e 100644 --- a/app/views/MainInfos.scala +++ b/app/views/MainInfos.scala @@ -1,4 +1,6 @@ package views +import modules.AppConfig + /** Contains infos that we want to pass to all views. */ -case class MainInfos(isDemo: Boolean, topHeaderWarningMessage: Option[String]) +case class MainInfos(isDemo: Boolean, config: AppConfig) diff --git a/app/views/allArea.scala.html b/app/views/allArea.scala.html index 61385350a..a8e5c0a40 100644 --- a/app/views/allArea.scala.html +++ b/app/views/allArea.scala.html @@ -1,6 +1,6 @@ @import models._ @import java.util.UUID -@(user: User, currentUserRights: Authorization.UserRights)(areas: List[Area], areasWithLoginByKey: List[UUID], userGroups: List[UserGroup])(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, request: RequestHeader, mainInfos: MainInfos) +@(user: User, currentUserRights: Authorization.UserRights)(areas: List[Area], userGroups: List[UserGroup])(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, request: RequestHeader, mainInfos: MainInfos) @main(user, currentUserRights)(s"Gestion des territoires"){ @@ -15,7 +15,7 @@ @area.name @if(user.admin) { / @area.id / - @if(areasWithLoginByKey.contains[UUID](area.id)) { + @if(mainInfos.config.areasWithLoginByKey.contains[UUID](area.id)) { warning Login par clé possiblewarning / } diff --git a/app/views/application.scala b/app/views/application.scala index 3c7a7c49a..290a0fc2a 100644 --- a/app/views/application.scala +++ b/app/views/application.scala @@ -5,6 +5,7 @@ import controllers.routes.{ApplicationController, Assets} import helpers.forms.CSRFInput import java.util.UUID import models.{Answer, Application, Area, Authorization, FileMetadata, User, UserGroup} +import modules.AppConfig import org.webjars.play.WebJarsUtil import play.api.mvc.RequestHeader import scalatags.Text.all._ @@ -17,15 +18,16 @@ object application { application: Application, currentUser: User, currentUserRights: Authorization.UserRights, - fileExpiryDayCount: Int, + config: AppConfig, ): Frag = { - val daysRemaining = Application.filesAvailabilityLeftInDays(fileExpiryDayCount)(application) + val daysRemaining = + Application.filesAvailabilityLeftInDays(config.filesExpirationInDays)(application) frag( files .filter(_.attached.isApplication) .map(file => fileLink( - Authorization.fileCanBeShowed(fileExpiryDayCount)(file.attached, application)( + Authorization.fileCanBeShowed(config.filesExpirationInDays)(file.attached, application)( currentUser.id, currentUserRights ), @@ -33,7 +35,8 @@ object application { daysRemaining, application.creatorUserName, "", - Some(file.status) + file.status, + config ) ) ) @@ -45,15 +48,15 @@ object application { application: Application, currentUser: User, currentUserRights: Authorization.UserRights, - fileExpiryDayCount: Int + config: AppConfig, ): Frag = { - val daysRemaining = Answer.filesAvailabilityLeftInDays(fileExpiryDayCount)(answer) + val daysRemaining = Answer.filesAvailabilityLeftInDays(config.filesExpirationInDays)(answer) frag( files .filter(_.attached.answerIdOpt === answer.id.some) .map(file => fileLink( - Authorization.fileCanBeShowed(fileExpiryDayCount)(file.attached, application)( + Authorization.fileCanBeShowed(config.filesExpirationInDays)(file.attached, application)( currentUser.id, currentUserRights ), @@ -61,7 +64,8 @@ object application { daysRemaining, answer.creatorUserName, "mdl-cell mdl-cell--12-col typography--text-align-center", - Some(file.status) + file.status, + config ) ) ) @@ -73,31 +77,32 @@ object application { daysRemaining: Option[Int], uploaderName: String, additionalClasses: String, - status: Option[FileMetadata.Status] + status: FileMetadata.Status, + config: AppConfig, ): Tag = { import FileMetadata.Status._ val link: Frag = - if (isAuthorized) - if (status.isEmpty || status === Some(Available)) + if (isAuthorized) { + if (status === Available) frag( "le fichier ", a(href := ApplicationController.file(metadata.id).url, metadata.filename) ) else s"le fichier ${metadata.filename}" - else + } else "un fichier" val statusMessage: Frag = status match { - case None | Some(Available) => + case Available => daysRemaining match { case None => "Fichier expiré et supprimé" case Some(expirationInDays) => s"Suppression du fichier dans $expirationInDays jours" } - case Some(Scanning) => "Scan par un antivirus en cours" - case Some(Quarantined) => "Fichier supprimé par l’antivirus" - case Some(Expired) => "Fichier expiré et supprimé" - case Some(Error) => + case Scanning => "Scan par un antivirus en cours" + case Quarantined => "Fichier supprimé par l’antivirus" + case Expired => "Fichier expiré et supprimé" + case Error => "Une erreur est survenue lors de l’envoi du fichier, celui-ci n’est pas disponible" } div( diff --git a/app/views/createApplication.scala.html b/app/views/createApplication.scala.html index 54e13de8a..257abb87c 100644 --- a/app/views/createApplication.scala.html +++ b/app/views/createApplication.scala.html @@ -5,7 +5,7 @@ @import play.api.libs.json.Json @import serializers.Keys -@(currentUser: User, currentUserRights: Authorization.UserRights, currentArea: Area)(instructorsOfGroups: List[User], organisationGroups: List[UserGroup], coworkers: List[User], userSignature: Option[String], canCreatePhoneMandat: Boolean, featureMandatSms: Boolean, featureCanSendApplicationsAnywhere: Boolean, categories: List[Category], applicationForm: Form[models.formModels.ApplicationFormData], attachments: List[FileMetadata] = Nil)(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, request: RequestHeader, messagesProvider: MessagesProvider, mainInfos: MainInfos) +@(currentUser: User, currentUserRights: Authorization.UserRights, currentArea: Area)(instructorsOfGroups: List[User], organisationGroups: List[UserGroup], coworkers: List[User], userSignature: Option[String], canCreatePhoneMandat: Boolean, categories: List[Category], applicationForm: Form[models.formModels.ApplicationFormData], attachments: List[FileMetadata] = Nil)(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, request: RequestHeader, messagesProvider: MessagesProvider, mainInfos: MainInfos) @main(currentUser, currentUserRights)(s"Nouvelle demande") { @@ -32,7 +32,7 @@

Nouvelle demande

* sont obligatoires - @if(featureCanSendApplicationsAnywhere) { + @if(mainInfos.config.featureCanSendApplicationsAnywhere) {
Territoire concerné
@toHtml(views.helpers.changeAreaSelect(currentArea, Area.all, routes.ApplicationController.create)) @@ -272,7 +272,7 @@

- @if(featureMandatSms) { + @if(mainInfos.config.featureMandatSms) {
@helpers.smsMandatAjaxForm()
diff --git a/app/views/helpers/common.scala b/app/views/helpers/common.scala index 00cc304eb..4d22e26ca 100644 --- a/app/views/helpers/common.scala +++ b/app/views/helpers/common.scala @@ -50,7 +50,7 @@ object common { def topHeaderConnected(implicit mainInfos: MainInfos): Frag = { val headerLines = List[Option[Frag]]( optionalDemoVersionHeader, - mainInfos.topHeaderWarningMessage.map(message => + mainInfos.config.topHeaderWarningMessage.map(message => div( cls := "mdl-layout__header-row mdl-color--deep-purple-400 mdl-color-text--white", message diff --git a/app/views/home/LoginPanel.scala b/app/views/home/LoginPanel.scala index e23091dd6..290b11ef1 100644 --- a/app/views/home/LoginPanel.scala +++ b/app/views/home/LoginPanel.scala @@ -12,7 +12,6 @@ object LoginPanel { case class EmailSentFeedback( email: String, timeZone: ZoneId, - tokenExpirationInMinutes: Int, successMessage: Option[String] ) extends LoginPanel diff --git a/app/views/home/emailSentFeedbackPanel.scala.html b/app/views/home/emailSentFeedbackPanel.scala.html index 12c6963ae..62990dddb 100644 --- a/app/views/home/emailSentFeedbackPanel.scala.html +++ b/app/views/home/emailSentFeedbackPanel.scala.html @@ -1,4 +1,4 @@ -@(infos: views.home.LoginPanel.EmailSentFeedback) +@(infos: views.home.LoginPanel.EmailSentFeedback)(implicit mainInfos: MainInfos)

@@ -6,9 +6,9 @@

- Nous avons envoyé par e-mail un lien de connexion sécurisée valable @infos.tokenExpirationInMinutes minutes + Nous avons envoyé par e-mail un lien de connexion sécurisée valable @mainInfos.config.tokenExpirationInMinutes minutes à l’adresse @infos.email. - Cliquez sur le lien contenu dans l’e-mail « @views.emails.common.magicLinkSubject » avant @{java.time.ZonedDateTime.now(infos.timeZone).plusMinutes(infos.tokenExpirationInMinutes.toLong).format(_root_.helper.Time.hourAndMinutesFormatter)} pour accéder à Administration+. + Cliquez sur le lien contenu dans l’e-mail « @views.emails.common.magicLinkSubject » avant @{java.time.ZonedDateTime.now(infos.timeZone).plusMinutes(mainInfos.config.tokenExpirationInMinutes.toLong).format(_root_.helper.Time.hourAndMinutesFormatter)} pour accéder à Administration+.

diff --git a/app/views/magicLinkAntiConsumptionPage.scala.html b/app/views/magicLinkAntiConsumptionPage.scala.html index 49ea88e7f..0e79b4d7c 100644 --- a/app/views/magicLinkAntiConsumptionPage.scala.html +++ b/app/views/magicLinkAntiConsumptionPage.scala.html @@ -1,6 +1,6 @@ @import helper._ -@(token: String, pathToRedirectTo: String, tokenExpirationInMinutes: Int)(implicit webJarsUtil: org.webjars.play.WebJarsUtil, mainInfos: MainInfos) +@(token: String, pathToRedirectTo: String)(implicit webJarsUtil: org.webjars.play.WebJarsUtil, mainInfos: MainInfos) diff --git a/app/views/showApplication.scala.html b/app/views/showApplication.scala.html index e3dfed899..a9fd6d84c 100644 --- a/app/views/showApplication.scala.html +++ b/app/views/showApplication.scala.html @@ -5,7 +5,7 @@ @import models.Answer.AnswerType @import serializers.Keys -@(currentUser: User, currentUserRights: Authorization.UserRights)(groupsWithUsersThatCanBeInvited: List[(UserGroup,List[User])], groupsThatCanBeInvited: List[UserGroup], application: Application, answerToAgentsForm: Form[_], openedTab: String, selectedArea: Area, userSignature: Option[String], attachments: List[FileMetadata], fileExpiryDayCount: Int)(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, messagesProvider: MessagesProvider, request: RequestHeader, mainInfos: MainInfos) +@(currentUser: User, currentUserRights: Authorization.UserRights)(groupsWithUsersThatCanBeInvited: List[(UserGroup,List[User])], groupsThatCanBeInvited: List[UserGroup], application: Application, answerToAgentsForm: Form[_], openedTab: String, selectedArea: Area, userSignature: Option[String], attachments: List[FileMetadata])(implicit webJarsUtil: org.webjars.play.WebJarsUtil, flash: Flash, messagesProvider: MessagesProvider, request: RequestHeader, mainInfos: MainInfos) @organisationIcon(creatorUserName: String) = { @Map("A+" -> "aplus", @@ -245,7 +245,7 @@

@application.subject

@for(mandatDate <- application.mandatDate) { (@mandatDate) } } - @toHtml(views.application.applicationFilesLinks(attachments, application, currentUser, currentUserRights, fileExpiryDayCount)) + @toHtml(views.application.applicationFilesLinks(attachments, application, currentUser, currentUserRights, mainInfos.config))
@@ -310,7 +310,7 @@

@application.subject

} @if(currentUser.instructor || currentUser.admin || currentUser.groupAdmin || answer.visibleByHelpers || answer.creatorUserID == currentUser.id) { - @toHtml(views.application.answerFilesLinks(attachments, answer, application, currentUser, currentUserRights, fileExpiryDayCount)) + @toHtml(views.application.answerFilesLinks(attachments, answer, application, currentUser, currentUserRights, mainInfos.config)) @if(answer.message.nonEmpty) {