~jan0sch/smederee
Showing details for patch 1748634ccfae207a0c1d69f656f5244ef644091e.
diff -rN -u old-smederee/build.sbt new-smederee/build.sbt --- old-smederee/build.sbt 2025-01-31 13:55:45.818185516 +0000 +++ new-smederee/build.sbt 2025-01-31 13:55:45.822185522 +0000 @@ -50,7 +50,7 @@ publish := {}, publishLocal := {} ) - .aggregate(darcs, email, hub, i18n, security, twirl) + .aggregate(darcs, email, htmlUtils, hub, i18n, security, twirl) lazy val darcs = project @@ -119,10 +119,28 @@ ) ) +lazy val htmlUtils = + project + .in(file("modules/html-utils")) + .enablePlugins(AutomateHeaderPlugin) + .settings(commonSettings) + .settings( + name := "html-utils", + version := "0.5.0-SNAPSHOT", + libraryDependencies ++= Seq( + library.catsCore, + library.http4sCore, + library.ip4sCore, + library.munit % Test, + library.munitScalaCheck % Test, + library.scalaCheck % Test + ) + ) + lazy val hub = project .in(file("modules/hub")) - .dependsOn(darcs, email, i18n, security, twirl) + .dependsOn(darcs, email, htmlUtils, i18n, security, twirl) .enablePlugins( AutomateHeaderPlugin, DebianPlugin, diff -rN -u old-smederee/modules/html-utils/src/main/scala/de/smederee/html/ExternalUrlConfiguration.scala new-smederee/modules/html-utils/src/main/scala/de/smederee/html/ExternalUrlConfiguration.scala --- old-smederee/modules/html-utils/src/main/scala/de/smederee/html/ExternalUrlConfiguration.scala 1970-01-01 00:00:00.000000000 +0000 +++ new-smederee/modules/html-utils/src/main/scala/de/smederee/html/ExternalUrlConfiguration.scala 2025-01-31 13:55:45.822185522 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Contributors as noted in the AUTHORS.md file + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.smederee.html + +import com.comcast.ip4s.{ Host, Port } +import org.http4s.Uri + +/** A wrapper for bundling values to configure a possible "external url mapping", the idea is that a service might be + * listening on a server and port which is behind a reverse proxy. The value configured here may have a multitude of + * applications generation of valid urls and adjusting of CSRF protection only being examples. + * + * @param host + * The official hostname of the service which could be used for the CSRF protection, generation of links in e-mails + * etc. + * @param path + * A possible path prefix that will be prepended to any paths used in link generation. + * @param port + * The port number which defaults to the port the service is listening on. Please note that this is also relevant for + * CSRF protection! It should not be defined if the service is running behind a reverse proxy listening on the + * standard port for the given URL scheme (http/https). + * @param scheme + * The URL scheme which is used for links and will also determine if cookies will have the secure flag enabled. + */ +final case class ExternalUrlConfiguration(host: Host, path: Option[Uri], port: Option[Port], scheme: Uri.Scheme) diff -rN -u old-smederee/modules/html-utils/src/main/scala/de/smederee/html/LinkTools.scala new-smederee/modules/html-utils/src/main/scala/de/smederee/html/LinkTools.scala --- old-smederee/modules/html-utils/src/main/scala/de/smederee/html/LinkTools.scala 1970-01-01 00:00:00.000000000 +0000 +++ new-smederee/modules/html-utils/src/main/scala/de/smederee/html/LinkTools.scala 2025-01-31 13:55:45.822185522 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Contributors as noted in the AUTHORS.md file + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.smederee.html + +import cats.syntax.all._ +import org.http4s.Uri + +object LinkTools { + + extension (linkConfig: ExternalUrlConfiguration) { + + /** Take the given URI path and create a full URI using the specified configuration with a possible path prefix and + * append the given path to it. + * + * @param path + * An URI containing a path with possible URL fragment and query parameters which will be used to construct the + * full URI. + * @return + * A full URI created from the values of the ExternalUrlConfiguration (scheme, host, port, possible path prefix) + * and the path data from the given URI. + */ + def createFullUri(path: Uri): Uri = { + val completePath = linkConfig.path match { + case None => path.path + case Some(pathPrefix) => pathPrefix.path |+| path.path + } + val baseUri = Uri( + scheme = Option(linkConfig.scheme), + authority = Option( + Uri.Authority( + userInfo = None, + host = Uri.Host.fromIp4sHost(linkConfig.host), + port = linkConfig.port.map(_.value) + ) + ), + path = completePath + ).withQueryParams(path.params) + path.fragment.fold(baseUri)(fragment => baseUri.withFragment(fragment)) + } + } + +} diff -rN -u old-smederee/modules/html-utils/src/main/scala/de/smederee/html/MetaTags.scala new-smederee/modules/html-utils/src/main/scala/de/smederee/html/MetaTags.scala --- old-smederee/modules/html-utils/src/main/scala/de/smederee/html/MetaTags.scala 1970-01-01 00:00:00.000000000 +0000 +++ new-smederee/modules/html-utils/src/main/scala/de/smederee/html/MetaTags.scala 2025-01-31 13:55:45.822185522 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 Contributors as noted in the AUTHORS.md file + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.smederee.html + +enum MetaRobotsDirective(val tag: String) { + case Follow extends MetaRobotsDirective("follow") + case Index extends MetaRobotsDirective("index") + case NoFollow extends MetaRobotsDirective("nofollow") + case NoIndex extends MetaRobotsDirective("noindex") +} + +opaque type MetaDescription = String +object MetaDescription { + + /** Create an instance of MetaDescription from the given String type. + * + * @param source + * An instance of type String which will be returned as a MetaDescription. + * @return + * The appropriate instance of MetaDescription. + */ + def apply(source: String): MetaDescription = source + + /** Try to create an instance of MetaDescription from the given String. + * + * @param source + * A String that should fulfil the requirements to be converted into a MetaDescription. + * @return + * An option to the successfully converted MetaDescription. + */ + def from(source: String): Option[MetaDescription] = Option(source) + +} + +opaque type MetaKeyWords = List[String] +object MetaKeyWords { + + /** Create an instance of MetaKeyWords from the given List[String] type. + * + * @param source + * An instance of type List[String] which will be returned as a MetaKeyWords. + * @return + * The appropriate instance of MetaKeyWords. + */ + def apply(source: List[String]): MetaKeyWords = source + + /** Return an empty instance of MetaKeyWords. + * + * @return + * An empty list. + */ + def empty: MetaKeyWords = List.empty + + /** Try to create an instance of MetaKeyWords from the given List[String]. + * + * @param source + * A List[String] that should fulfil the requirements to be converted into a MetaKeyWords. + * @return + * An option to the successfully converted MetaKeyWords. + */ + def from(source: List[String]): Option[MetaKeyWords] = + source.flatMap(string => Option(string)) match { + case Nil => None + case keywords => Option(keywords) + } + + extension (keywords: MetaKeyWords) { + def isEmpty: Boolean = keywords.isEmpty + def mkString: String = keywords.toList.mkString(", ") + def nonEmpty: Boolean = keywords.nonEmpty + } + +} + +/** HTML meta attributes which can be written into the header part of an HTML page. + * + * @param description + * An optional description for the related meta tag which should not be too long (max. 160/200 characters). + * @param keywords + * A list of keywords which can be empty and should not be too long. + */ +final case class MetaTags(description: Option[MetaDescription], keywords: MetaKeyWords) + +object MetaTags { + + /** Return an empty meta tags instance. + * + * @return + * An instance of meta tags containing no values, resulting in no tags being rendered. + */ + def empty: MetaTags = MetaTags(description = None, keywords = MetaKeyWords.empty) +} diff -rN -u old-smederee/modules/html-utils/src/test/scala/de/smederee/html/LinkToolsTest.scala new-smederee/modules/html-utils/src/test/scala/de/smederee/html/LinkToolsTest.scala --- old-smederee/modules/html-utils/src/test/scala/de/smederee/html/LinkToolsTest.scala 1970-01-01 00:00:00.000000000 +0000 +++ new-smederee/modules/html-utils/src/test/scala/de/smederee/html/LinkToolsTest.scala 2025-01-31 13:55:45.822185522 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Contributors as noted in the AUTHORS.md file + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.smederee.html + +import com.comcast.ip4s._ +import org.http4s.Uri +import org.http4s.implicits._ + +import munit._ +import org.scalacheck._ +import de.smederee.html.LinkTools.createFullUri + +final class LinkToolsTest extends ScalaCheckSuite { + + val externalUrlConfig = ExternalUrlConfiguration( + host"proxy.example.com", + Uri.fromString("sub-path/with/children").toOption, + None, + Uri.Scheme.https + ) + + test("createFullUri must create a corrent full Uri") { + val uri = uri"/another/path#fragment?parameter=value&another=one" + val fullUri = externalUrlConfig.createFullUri(uri) + assertEquals( + fullUri.renderString, + "https://proxy.example.com/sub-path/with/children/another/path#fragment?parameter=value&another=one" + ) + } +}