All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mchange.feedletter.AppSetup.scala Maven / Gradle / Ivy

package com.mchange.feedletter

import zio.*
import java.io.{BufferedInputStream,InputStream}
import java.nio.file.{Path as JPath}
import java.util.Properties
import scala.util.Using
import scala.jdk.CollectionConverters.*

import com.mchange.mailutil.Smtp
import com.mchange.conveniences.javautil.*

object AppSetup:

  val AcceptableSecretsPermStrings = Set("r--------","rw-------")
  val AcceptableSecretsPerms = AcceptableSecretsPermStrings.map( os.PermSet.fromString )

  val DefaultSecretsFileName = "feedletter-secrets.properties"

  val DefaultSecretsSearch =
    ((os.pwd / DefaultSecretsFileName).toString :: s"/etc/feedletter/${DefaultSecretsFileName}" :: s"/usr/local/etc/feedletter/${DefaultSecretsFileName}" :: Nil).map( os.Path.apply )

  def julConfigRawInputStreamAndSource() : (InputStream, LoggingConfig) =
    try
      (os.read.inputStream( os.resource / "logging.properties" ), LoggingConfig.User)
    catch
      case rnfe : os.ResourceNotFoundException =>
        (os.read.inputStream( os.resource / "feedletter-default-logging.properties" ), LoggingConfig.Default)
    end try

  private val julAppSetup : Task[LoggingConfig] = ZIO.attemptBlocking:
    val ( ris, source ) = julConfigRawInputStreamAndSource()
    Using.resource( new BufferedInputStream( ris ) ): is =>
      java.util.logging.LogManager.getLogManager().readConfiguration( is )
    source

  def live( secrets : Option[JPath] ) : ZLayer[Any, Throwable, AppSetup] = ZLayer.fromZIO:
    for
      lcfg <- julAppSetup
    yield
      val (loc : Option[os.Path], props : Properties) =
        secrets match
          case Some( jpath ) => ( Some(os.Path(jpath)), loadProperties( jpath ) )
          case None =>
            DefaultSecretsSearch.find( os.exists ).fold( (None, new Properties()) )( path => (Some(path), loadProperties(path.toIO)) )
      loc.foreach: p =>
        val perms : os.PermSet = os.perms(p)
        if (!AcceptableSecretsPerms(perms))
          val ap = AcceptableSecretsPermStrings.mkString(", ")
          throw new LeakySecrets(s"Secrets file '${p}' is not secret enough. Permission '${perms}'. Acceptable permissions: [$ap]")
      val propsMap = props.toMap
      AppSetup( loc, propsMap, lcfg )

case class AppSetup( secretsLoc : Option[os.Path], secrets : Map[String,String], loggingConfig : LoggingConfig ):
  lazy val smtpContext : Smtp.Context = Smtp.Context( (Smtp.Context.defaultProperties().asScala.toMap ++ secrets).toProperties, sys.env )
  lazy val secretSalt : String = secrets.get("feedletter.secret.salt").getOrElse:
    throw new NoSecretSalt("Please set 'feedletter.secret.salt' to an arbitrary but consistent String in the feedletter-secrets file.")
  lazy val pidFile : os.Path = secrets.get("feedletter.pid.file").map( os.Path.apply ).getOrElse( os.pwd / "feedletter.pid" )




© 2015 - 2025 Weber Informatics LLC | Privacy Policy