Add pairgoth-common module to share sources between api and view webapps
This commit is contained in:
150
pairgoth-common/pom.xml
Normal file
150
pairgoth-common/pom.xml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.jeudego.pairgoth</groupId>
|
||||
<artifactId>engine-parent</artifactId>
|
||||
<version>0.3</version>
|
||||
</parent>
|
||||
<artifactId>pairgoth-common</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.groupId}:${project.artifactId}</name>
|
||||
<description>Pairgoth common utilities</description>
|
||||
<url>TODO</url>
|
||||
<build>
|
||||
<defaultGoal>package</defaultGoal>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.9.3</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- main dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit5</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-datetime-jvm</artifactId>
|
||||
<version>0.4.0</version>
|
||||
</dependency>
|
||||
<!-- servlets and mail APIs -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>${servlet.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>jakarta.mail</artifactId>
|
||||
<version>1.6.7</version>
|
||||
</dependency>
|
||||
<!-- utils -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.13.0</version>
|
||||
</dependency>
|
||||
<!-- logging -->
|
||||
<dependency>
|
||||
<groupId>io.github.microutils</groupId>
|
||||
<artifactId>kotlin-logging-jvm</artifactId>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.diogonunes</groupId>
|
||||
<artifactId>JColor</artifactId>
|
||||
<version>5.0.1</version>
|
||||
</dependency>
|
||||
<!-- mailer -->
|
||||
<dependency>
|
||||
<groupId>com.republicate</groupId>
|
||||
<artifactId>simple-mailer</artifactId>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
<!-- json -->
|
||||
<dependency>
|
||||
<groupId>com.republicate.kson</groupId>
|
||||
<artifactId>essential-kson-jvm</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
<!-- tests -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito.kotlin</groupId>
|
||||
<artifactId>mockito-kotlin</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
307
pairgoth-common/pom.xml.versionsBackup
Normal file
307
pairgoth-common/pom.xml.versionsBackup
Normal file
@@ -0,0 +1,307 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.jeudego.pairgoth</groupId>
|
||||
<artifactId>engine-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>api-webapp</artifactId>
|
||||
|
||||
<packaging>war</packaging>
|
||||
<name>${project.groupId}:${project.artifactId}</name>
|
||||
<description>PairGoth pairing system</description>
|
||||
<url>TODO</url>
|
||||
<properties>
|
||||
<pac4j.version>5.7.1</pac4j.version>
|
||||
</properties>
|
||||
<build>
|
||||
<defaultGoal>package</defaultGoal>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<classpathDependencyExcludes>
|
||||
<classpathDependencyExclude>com.republicate:webapp-slf4j-logger</classpathDependencyExclude>
|
||||
</classpathDependencyExcludes>
|
||||
<systemPropertyVariables>
|
||||
<test.build.dir>${project.build.testOutputDirectory}</test.build.dir>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
<configuration>
|
||||
<scan>0</scan>
|
||||
<httpConnector>
|
||||
<host>${pairgoth.api.host}</host>
|
||||
<port>${pairgoth.api.port}</port>
|
||||
</httpConnector>
|
||||
<systemProperties>
|
||||
<pairgoth.env>${pairgoth.env}</pairgoth.env>
|
||||
<pairgoth.version>${pairgoth.version}</pairgoth.version>
|
||||
<pairgoth.api.external.url>${pairgoth.api.external.url}</pairgoth.api.external.url>
|
||||
<pairgoth.webapp.external.url>${pairgoth.webapp.external.url}</pairgoth.webapp.external.url>
|
||||
<pairgoth.store>${pairgoth.store}</pairgoth.store>
|
||||
<pairgoth.store.file.path>${pairgoth.store.file.path}</pairgoth.store.file.path>
|
||||
<pairgoth.logger.level>${pairgoth.logger.level}</pairgoth.logger.level>
|
||||
<pairgoth.logger.format>${pairgoth.logger.format}</pairgoth.logger.format>
|
||||
</systemProperties>
|
||||
<webApp>
|
||||
<contextPath>${pairgoth.api.context}/</contextPath>
|
||||
</webApp>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>jaxb2-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>gen-schema</id>
|
||||
<goals>
|
||||
<goal>xjc</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<packageName>org.jeudego.pairgoth.opengotha</packageName>
|
||||
<sourceType>XmlSchema</sourceType>
|
||||
<sources>
|
||||
<source>src/main/resources/xsd</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.9.3</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- main dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit5</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-datetime-jvm</artifactId>
|
||||
<version>0.4.0</version>
|
||||
</dependency>
|
||||
<!-- servlets and mail APIs -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>${servlet.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>jakarta.mail</artifactId>
|
||||
<version>1.6.7</version>
|
||||
</dependency>
|
||||
<!-- utils -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.13.0</version>
|
||||
</dependency>
|
||||
<!-- auth -->
|
||||
<dependency>
|
||||
<groupId>org.pac4j</groupId>
|
||||
<artifactId>pac4j-oauth</artifactId>
|
||||
<version>${pac4j.version}</version>
|
||||
</dependency>
|
||||
<!-- logging -->
|
||||
<dependency>
|
||||
<groupId>io.github.microutils</groupId>
|
||||
<artifactId>kotlin-logging-jvm</artifactId>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.republicate</groupId>
|
||||
<artifactId>webapp-slf4j-logger</artifactId>
|
||||
<version>3.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.diogonunes</groupId>
|
||||
<artifactId>JColor</artifactId>
|
||||
<version>5.0.1</version>
|
||||
</dependency>
|
||||
<!-- mailer -->
|
||||
<dependency>
|
||||
<groupId>com.republicate</groupId>
|
||||
<artifactId>simple-mailer</artifactId>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
<!-- json -->
|
||||
<dependency>
|
||||
<groupId>com.republicate.kson</groupId>
|
||||
<artifactId>essential-kson-jvm</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
<!-- charset detection
|
||||
<dependency>
|
||||
<groupId>com.ibm.icu</groupId>
|
||||
<artifactId>icu4j</artifactId>
|
||||
<version>70.1</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- net clients
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.13</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- pdf -->
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>pdfbox</artifactId>
|
||||
<version>2.0.28</version>
|
||||
</dependency>
|
||||
<!-- server-side events -->
|
||||
<dependency>
|
||||
<groupId>com.republicate</groupId>
|
||||
<artifactId>jeasse-servlet3</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<!-- graph solver -->
|
||||
<dependency>
|
||||
<groupId>org.jgrapht</groupId>
|
||||
<artifactId>jgrapht-core</artifactId>
|
||||
<version>1.5.2</version>
|
||||
</dependency>
|
||||
<!-- xml class generation -->
|
||||
<dependency>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<version>4.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
<!-- tests -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito.kotlin</groupId>
|
||||
<artifactId>mockito-kotlin</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- test emails -->
|
||||
<dependency>
|
||||
<groupId>com.icegreen</groupId>
|
||||
<artifactId>greenmail</artifactId>
|
||||
<version>1.6.12</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,57 @@
|
||||
package org.jeudego.pairgoth.util
|
||||
|
||||
import java.nio.charset.Charset
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.Cipher.DECRYPT_MODE
|
||||
import javax.crypto.Cipher.ENCRYPT_MODE
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
|
||||
/**
|
||||
* Basic AES encryption. Please note that it uses the ECB block mode, which has the advantage
|
||||
* to not require random bytes, thus providing some *persistence* for the encrypted data, but
|
||||
* at the expense of some security weaknesses. The purpose here is just to encrypt temporary
|
||||
* session ids in URLs, not to protect state secrets.
|
||||
*/
|
||||
class AESCryptograph : Cryptograph {
|
||||
|
||||
override fun init(key: String) {
|
||||
val bytes = key.toByteArray(Charset.defaultCharset())
|
||||
if (bytes.size < 16) {
|
||||
throw Error("not enough secret bytes")
|
||||
}
|
||||
val secret: SecretKey = SecretKeySpec(bytes, 0, 16, ALGORITHM)
|
||||
try {
|
||||
encrypt.init(ENCRYPT_MODE, secret)
|
||||
decrypt.init(DECRYPT_MODE, secret)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("cyptograph initialization failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encrypt(str: String): ByteArray {
|
||||
return try {
|
||||
encrypt.doFinal(str.toByteArray(Charset.defaultCharset()))
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("encryption failed failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun decrypt(bytes: ByteArray): String {
|
||||
return try {
|
||||
String(decrypt.doFinal(bytes), Charset.defaultCharset())
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("encryption failed failed", e)
|
||||
}
|
||||
}
|
||||
|
||||
private var encrypt = Cipher.getInstance(CIPHER)
|
||||
private var decrypt = Cipher.getInstance(CIPHER)
|
||||
|
||||
companion object {
|
||||
private val CIPHER = "AES/ECB/PKCS5Padding"
|
||||
private val ALGORITHM = "AES"
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package org.jeudego.pairgoth.util
|
||||
|
||||
import com.diogonunes.jcolor.Ansi
|
||||
import com.diogonunes.jcolor.AnsiFormat
|
||||
import com.diogonunes.jcolor.Attribute
|
||||
|
||||
private val blue = AnsiFormat(Attribute.BRIGHT_BLUE_TEXT())
|
||||
private val green = AnsiFormat(Attribute.BRIGHT_GREEN_TEXT())
|
||||
private val red = AnsiFormat(Attribute.BRIGHT_RED_TEXT())
|
||||
private val bold = AnsiFormat(Attribute.BOLD())
|
||||
|
||||
object Colorizer {
|
||||
|
||||
fun blue(str: String) = Ansi.colorize(str, blue)
|
||||
fun green(str: String) = Ansi.colorize(str, green)
|
||||
fun red(str: String) = Ansi.colorize(str, red)
|
||||
fun bold(str: String) = Ansi.colorize(str, bold)
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package org.jeudego.pairgoth.util
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* Cryptograph - used to encrypt and decrypt strings.
|
||||
*
|
||||
*/
|
||||
interface Cryptograph : Serializable {
|
||||
/**
|
||||
* init.
|
||||
* @param random random string
|
||||
*/
|
||||
fun init(random: String)
|
||||
|
||||
/**
|
||||
* encrypt.
|
||||
* @param str string to encrypt
|
||||
* @return encrypted string
|
||||
*/
|
||||
fun encrypt(str: String): ByteArray
|
||||
|
||||
/**
|
||||
* decrypt.
|
||||
* @param bytes to decrypt
|
||||
* @return decrypted string
|
||||
*/
|
||||
fun decrypt(bytes: ByteArray): String
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package org.jeudego.pairgoth.util
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import java.io.Reader
|
||||
import java.io.Writer
|
||||
|
||||
|
||||
fun Json.Companion.parse(reader: Reader) = Json.Companion.parse(object: Json.Input {
|
||||
override fun read() = reader.read().toChar()
|
||||
})
|
||||
|
||||
fun Json.toString(writer: Writer) = toString(object: Json.Output {
|
||||
override fun writeChar(c: Char): Json.Output {
|
||||
writer.write(c.code)
|
||||
return this
|
||||
}
|
||||
override fun writeString(s: String): Json.Output {
|
||||
writer.write(s)
|
||||
return this
|
||||
}
|
||||
override fun writeString(s: String, from: Int, to: Int): Json.Output {
|
||||
writer.write(s, from, to)
|
||||
return this
|
||||
}
|
||||
})
|
@@ -0,0 +1,556 @@
|
||||
package org.jeudego.pairgoth.util
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.w3c.dom.*
|
||||
import org.xml.sax.ErrorHandler
|
||||
import org.xml.sax.InputSource
|
||||
import org.xml.sax.SAXException
|
||||
import org.xml.sax.SAXParseException
|
||||
import java.io.Reader
|
||||
import java.io.StringReader
|
||||
import java.io.StringWriter
|
||||
import java.lang.ref.SoftReference
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import java.util.concurrent.LinkedBlockingDeque
|
||||
import javax.xml.XMLConstants
|
||||
import javax.xml.parsers.DocumentBuilder
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.parsers.ParserConfigurationException
|
||||
import javax.xml.transform.OutputKeys
|
||||
import javax.xml.transform.TransformerException
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
import javax.xml.xpath.XPathConstants
|
||||
import javax.xml.xpath.XPathExpressionException
|
||||
import javax.xml.xpath.XPathFactory
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Utility class for simplifying parsing of xml documents. Documents are not validated, and
|
||||
* loading of external files (xinclude, external entities, DTDs, etc.) are disabled.
|
||||
*
|
||||
* @author Claude Brisson
|
||||
*/
|
||||
object XmlUtils {
|
||||
/* several pieces of code were borrowed from the Apache Shindig XmlUtil class.*/
|
||||
private val LOGGER = LoggerFactory.getLogger(XmlUtils::class.java)
|
||||
|
||||
/**
|
||||
* Handles xml errors so that they're not logged to stderr.
|
||||
*/
|
||||
private val errorHandler: ErrorHandler = object : ErrorHandler {
|
||||
@Throws(SAXException::class)
|
||||
override fun error(exception: SAXParseException) {
|
||||
throw exception
|
||||
}
|
||||
|
||||
@Throws(SAXException::class)
|
||||
override fun fatalError(exception: SAXParseException) {
|
||||
throw exception
|
||||
}
|
||||
|
||||
override fun warning(exception: SAXParseException) {
|
||||
LOGGER.info("warning during parsing", exception)
|
||||
}
|
||||
}
|
||||
|
||||
private var canReuseBuilders = false
|
||||
private val builderFactory = createDocumentBuilderFactory()
|
||||
private fun createDocumentBuilderFactory(): DocumentBuilderFactory {
|
||||
val builderFactory = DocumentBuilderFactory.newInstance()
|
||||
// Namespace support is required for <os:> elements
|
||||
builderFactory.isNamespaceAware = true
|
||||
|
||||
// Disable various insecure and/or expensive options.
|
||||
builderFactory.isValidating = false
|
||||
|
||||
// Can't disable doctypes entirely because they're usually harmless. External entity
|
||||
// resolution, however, is both expensive and insecure.
|
||||
try {
|
||||
builderFactory.setAttribute("http://xml.org/sax/features/external-general-entities", false)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Not supported by some very old parsers.
|
||||
LOGGER.info("Error parsing external general entities: ", e)
|
||||
}
|
||||
try {
|
||||
builderFactory.setAttribute("http://xml.org/sax/features/external-parameter-entities", false)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Not supported by some very old parsers.
|
||||
LOGGER.info("Error parsing external parameter entities: ", e)
|
||||
}
|
||||
try {
|
||||
builderFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Only supported by Apache's XML parsers.
|
||||
LOGGER.info("Error parsing external DTD: ", e)
|
||||
}
|
||||
try {
|
||||
builderFactory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Not supported by older parsers.
|
||||
LOGGER.info("Error parsing secure XML: ", e)
|
||||
}
|
||||
return builderFactory
|
||||
}
|
||||
|
||||
private val reusableBuilder: ThreadLocal<DocumentBuilder> = object : ThreadLocal<DocumentBuilder>() {
|
||||
override fun initialValue(): DocumentBuilder {
|
||||
return try {
|
||||
LOGGER.trace("Created a new document builder")
|
||||
builderFactory.newDocumentBuilder()
|
||||
} catch (e: ParserConfigurationException) {
|
||||
throw Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
val builder = builderFactory.newDocumentBuilder()
|
||||
builder.reset()
|
||||
canReuseBuilders = true
|
||||
LOGGER.trace("reusing document builders")
|
||||
} catch (e: UnsupportedOperationException) {
|
||||
// Only supported by newer parsers (xerces 2.8.x+ for instance).
|
||||
canReuseBuilders = false
|
||||
LOGGER.trace("not reusing document builders")
|
||||
} catch (e: ParserConfigurationException) {
|
||||
// Only supported by newer parsers (xerces 2.8.x+ for instance).
|
||||
canReuseBuilders = false
|
||||
LOGGER.trace("not reusing document builders")
|
||||
}
|
||||
}
|
||||
|
||||
private val builderPool = LinkedBlockingDeque<SoftReference<DocumentBuilder?>>() // contains only idle builders
|
||||
private val maxBuildersCount = 100
|
||||
private var currentBuildersCount = 0
|
||||
|
||||
/**
|
||||
* Get a document builder
|
||||
* @return document builder
|
||||
*/
|
||||
@Synchronized
|
||||
private fun getDocumentBuilder(): DocumentBuilder {
|
||||
var builder: DocumentBuilder? = null
|
||||
if (canReuseBuilders && builderPool.size > 0) {
|
||||
builder = builderPool.pollFirst().get()
|
||||
}
|
||||
if (builder == null) {
|
||||
if (!canReuseBuilders || currentBuildersCount < maxBuildersCount) {
|
||||
try {
|
||||
builder = builderFactory.newDocumentBuilder()
|
||||
builder.setErrorHandler(errorHandler)
|
||||
++currentBuildersCount
|
||||
} catch (e: Exception) {
|
||||
/* this is a fatal error */
|
||||
throw Error("could not create a new XML DocumentBuilder instance", e)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
LOGGER.warn(
|
||||
"reached XML DocumentBuilder pool size limit, current thread needs to wait",
|
||||
)
|
||||
builder = builderPool.takeFirst().get()
|
||||
} catch (ie: InterruptedException) {
|
||||
LOGGER.warn("caught an InterruptedException while waiting for a DocumentBuilder instance")
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder ?: throw Error("could not create a new XML DocumentBuilder instance")
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the given document builder
|
||||
* @param builder document builder
|
||||
*/
|
||||
@Synchronized
|
||||
private fun releaseBuilder(builder: DocumentBuilder?) {
|
||||
builder!!.reset()
|
||||
builderPool.addLast(SoftReference(builder))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty document
|
||||
*/
|
||||
fun createDocument(): Document {
|
||||
val builder = getDocumentBuilder()
|
||||
val doc = builder.newDocument()
|
||||
releaseBuilder(builder)
|
||||
return doc
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an attribute from a node.
|
||||
*
|
||||
* @param node target node
|
||||
* @param attr attribute name
|
||||
* @param def default value
|
||||
* @return The value of the attribute, or def
|
||||
*/
|
||||
fun getAttribute(node: Node, attr: String?, def: String?): String? {
|
||||
val attrs = node.attributes
|
||||
val `val` = attrs.getNamedItem(attr)
|
||||
return if (`val` != null) {
|
||||
`val`.nodeValue
|
||||
} else def
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node target node
|
||||
* @param attr attribute name
|
||||
* @return The value of the given attribute, or null if not present.
|
||||
*/
|
||||
fun getAttribute(node: Node, attr: String?): String? {
|
||||
return getAttribute(node, attr, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an attribute as a boolean.
|
||||
*
|
||||
* @param node target node
|
||||
* @param attr attribute name
|
||||
* @param def default value
|
||||
* @return True if the attribute exists and is not equal to "false"
|
||||
* false if equal to "false", and def if not present.
|
||||
*/
|
||||
fun getBoolAttribute(node: Node, attr: String?, def: Boolean): Boolean {
|
||||
val value = getAttribute(node, attr) ?: return def
|
||||
return java.lang.Boolean.parseBoolean(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node target node
|
||||
* @param attr attribute name
|
||||
* @return True if the attribute exists and is not equal to "false"
|
||||
* false otherwise.
|
||||
*/
|
||||
fun getBoolAttribute(node: Node, attr: String?): Boolean {
|
||||
return getBoolAttribute(node, attr, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node target node
|
||||
* @param attr attribute name
|
||||
* @param def default value
|
||||
* @return An attribute coerced to an integer.
|
||||
*/
|
||||
fun getIntAttribute(node: Node, attr: String?, def: Int): Int {
|
||||
val value = getAttribute(node, attr) ?: return def
|
||||
return try {
|
||||
value.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
def
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node target node
|
||||
* @param attr attribute name
|
||||
* @return An attribute coerced to an integer.
|
||||
*/
|
||||
fun getIntAttribute(node: Node, attr: String?): Int {
|
||||
return getIntAttribute(node, attr, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse the input xml into a single element.
|
||||
* @param xml xml stream reader
|
||||
* @return The document object
|
||||
*/
|
||||
fun parse(xml: Reader): Element {
|
||||
val builder = getDocumentBuilder()
|
||||
try {
|
||||
val doc = builder.parse(InputSource(xml))
|
||||
return doc.documentElement
|
||||
} finally {
|
||||
releaseBuilder(builder)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse the input xml into a single element.
|
||||
* @param xml xml string
|
||||
* @return The document object
|
||||
*/
|
||||
fun parse(xml: String): Element = parse(StringReader(xml.trim()))
|
||||
|
||||
/**
|
||||
* Search for nodes using an XPath expression
|
||||
* @param xpath XPath expression
|
||||
* @param context evaluation context
|
||||
* @return org.w3c.NodeList of found nodes
|
||||
* @throws XPathExpressionException
|
||||
*/
|
||||
@Throws(XPathExpressionException::class)
|
||||
fun search(xpath: String?, context: Node?): NodeList {
|
||||
val xp = XPathFactory.newInstance().newXPath()
|
||||
val exp = xp.compile(xpath)
|
||||
return exp.evaluate(context, XPathConstants.NODESET) as NodeList
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for nodes using an XPath expression
|
||||
* @param xpath XPath expression
|
||||
* @param context evaluation context
|
||||
* @return List of found nodes
|
||||
* @throws XPathExpressionException
|
||||
*/
|
||||
@Throws(XPathExpressionException::class)
|
||||
fun getNodes(xpath: String?, context: Node?): List<Node> {
|
||||
val ret: MutableList<Node> = ArrayList()
|
||||
val lst = search(xpath, context)
|
||||
for (i in 0 until lst.length) {
|
||||
ret.add(lst.item(i))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for elements using an XPath expression
|
||||
* @param xpath XPath expression
|
||||
* @param context evaluation context
|
||||
* @return List of found elements
|
||||
* @throws XPathExpressionException
|
||||
*/
|
||||
@Throws(XPathExpressionException::class)
|
||||
fun getElements(xpath: String?, context: Node?): List<Element> {
|
||||
val ret: MutableList<Element> = ArrayList()
|
||||
val lst = search(xpath, context)
|
||||
for (i in 0 until lst.length) {
|
||||
// will throw a ClassCastExpression if Node is not an Element,
|
||||
// that's what we want
|
||||
ret.add(lst.item(i) as Element)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Builds the xpath expression for a node (tries to use id/name nodes when possible to get a unique path)
|
||||
* @param n target node
|
||||
* @return node xpath
|
||||
*/
|
||||
// (borrow from http://stackoverflow.com/questions/5046174/get-xpath-from-the-org-w3c-dom-node )
|
||||
fun nodePath(n: Node): String {
|
||||
|
||||
// declarations
|
||||
var parent: Node?
|
||||
val hierarchy = Stack<Node>()
|
||||
val buffer = StringBuffer("/")
|
||||
|
||||
// push element on stack
|
||||
hierarchy.push(n)
|
||||
parent = when (n.nodeType) {
|
||||
Node.ATTRIBUTE_NODE -> (n as Attr).ownerElement
|
||||
Node.COMMENT_NODE, Node.ELEMENT_NODE, Node.DOCUMENT_NODE -> n.parentNode
|
||||
else -> throw IllegalStateException("Unexpected Node type" + n.nodeType)
|
||||
}
|
||||
while (null != parent && parent.nodeType != Node.DOCUMENT_NODE) {
|
||||
// push on stack
|
||||
hierarchy.push(parent)
|
||||
|
||||
// get parent of parent
|
||||
parent = parent.parentNode
|
||||
}
|
||||
|
||||
// construct xpath
|
||||
var obj: Any? = null
|
||||
while (!hierarchy.isEmpty() && null != hierarchy.pop().also { obj = it }) {
|
||||
val node = obj as Node?
|
||||
var handled = false
|
||||
if (node!!.nodeType == Node.ELEMENT_NODE) {
|
||||
val e = node as Element?
|
||||
|
||||
// is this the root element?
|
||||
if (buffer.length == 1) {
|
||||
// root element - simply append element name
|
||||
buffer.append(node.nodeName)
|
||||
} else {
|
||||
// child element - append slash and element name
|
||||
buffer.append("/")
|
||||
buffer.append(node.nodeName)
|
||||
if (node.hasAttributes()) {
|
||||
// see if the element has a name or id attribute
|
||||
if (e!!.hasAttribute("id")) {
|
||||
// id attribute found - use that
|
||||
buffer.append("[@id='" + e.getAttribute("id") + "']")
|
||||
handled = true
|
||||
} else if (e.hasAttribute("name")) {
|
||||
// name attribute found - use that
|
||||
buffer.append("[@name='" + e.getAttribute("name") + "']")
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
if (!handled) {
|
||||
// no known attribute we could use - get sibling index
|
||||
var prev_siblings = 1
|
||||
var prev_sibling = node.previousSibling
|
||||
while (null != prev_sibling) {
|
||||
if (prev_sibling.nodeType == node.nodeType) {
|
||||
if (prev_sibling.nodeName.equals(
|
||||
node.nodeName, ignoreCase = true
|
||||
)
|
||||
) {
|
||||
prev_siblings++
|
||||
}
|
||||
}
|
||||
prev_sibling = prev_sibling.previousSibling
|
||||
}
|
||||
buffer.append("[$prev_siblings]")
|
||||
}
|
||||
}
|
||||
} else if (node.nodeType == Node.ATTRIBUTE_NODE) {
|
||||
buffer.append("/@")
|
||||
buffer.append(node.nodeName)
|
||||
}
|
||||
}
|
||||
// return buffer
|
||||
return buffer.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* XML Node to string
|
||||
* @param node XML node
|
||||
* @return XML node string representation
|
||||
*/
|
||||
fun nodeToString(node: Node?, encoding: Charset = Charsets.UTF_8): String {
|
||||
val sw = StringWriter()
|
||||
try {
|
||||
val t = TransformerFactory.newInstance().newTransformer()
|
||||
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no")
|
||||
t.setOutputProperty(OutputKeys.INDENT, "no")
|
||||
t.setOutputProperty(OutputKeys.ENCODING, encoding.name())
|
||||
t.transform(DOMSource(node), StreamResult(sw))
|
||||
} catch (te: TransformerException) {
|
||||
LOGGER.error("could not convert XML node to string", te)
|
||||
}
|
||||
return sw.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* XML Node to string
|
||||
* @param node XML node
|
||||
* @return XML node string representation
|
||||
*/
|
||||
fun nodeToPrettyString(node: Node, encoding: Charset = Charsets.UTF_8): String {
|
||||
val sw = StringWriter()
|
||||
try {
|
||||
val t = TransformerFactory.newInstance().newTransformer()
|
||||
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no")
|
||||
t.setOutputProperty(OutputKeys.INDENT, "yes")
|
||||
t.setOutputProperty(OutputKeys.ENCODING, encoding.name())
|
||||
t.transform(DOMSource(node), StreamResult(sw))
|
||||
} catch (te: TransformerException) {
|
||||
LOGGER.error("could not convert XML node to string", te)
|
||||
}
|
||||
return sw.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkes whether the given mime type is an XML format
|
||||
* @param mimeType mime type
|
||||
* @return `true` if this mime type is an XML format
|
||||
*/
|
||||
fun isXmlMimeType(mimeType: String?): Boolean {
|
||||
return mimeType != null &&
|
||||
("text/xml" == mimeType || "application/xml" == mimeType ||
|
||||
mimeType.endsWith("+xml"))
|
||||
}
|
||||
}
|
||||
|
||||
// utility extension functions
|
||||
|
||||
fun emptyDocument(root: String) = XmlUtils.createDocument().also { it.appendChild(it.createElement(root)) }
|
||||
|
||||
fun Node.element(): Element =
|
||||
when (this) {
|
||||
is Element -> this
|
||||
is Document -> documentElement
|
||||
else -> throw Error("invalid xml node")
|
||||
}
|
||||
|
||||
fun Element.children(): List<Element> {
|
||||
val ret = mutableListOf<Element>()
|
||||
for (i in 0..childNodes.length) {
|
||||
val child = childNodes[i]
|
||||
if (child is Element) ret.add(child)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
fun Node.document(): Document = ownerDocument ?: this as Document
|
||||
|
||||
fun Element.childOrNull(key: String): Element? = children().firstOrNull { it.tagName == key }
|
||||
|
||||
fun Element.child(key: String): Element = childOrNull(key) ?: addChild(key)
|
||||
|
||||
fun Node.addChild(tag: String): Element = appendChild(document().createElement(tag)) as Element
|
||||
|
||||
fun Node.value(): String? = textContent.let { if (it.isEmpty()) null else it }
|
||||
|
||||
fun Node.attr(key: String, def: String? = null) = attributes.getNamedItem(key)?.nodeValue ?: def
|
||||
|
||||
fun Element.setAttr(key: String, value: Any) {
|
||||
setAttribute(key, value.toString())
|
||||
}
|
||||
|
||||
fun Node.boolAttr(key: String, def: Boolean? = null) = attr(key)?.toBoolean() ?: def
|
||||
|
||||
fun Node.intAttr(key: String, def: Int? = null) = attr(key)?.toInt() ?: def
|
||||
|
||||
fun Node.longAttr(key: String, def: Long? = null) = attr(key)?.toLong() ?: def
|
||||
|
||||
fun Node.doubleAttr(key: String, def: Double? = null) = attr(key)?.toDouble() ?: def
|
||||
|
||||
fun Node.path() = XmlUtils.nodePath(this)
|
||||
|
||||
fun Node.find(xpath: String): NodeList {
|
||||
return XPathFactory.newInstance().newXPath().compile(xpath).evaluate(this, XPathConstants.NODESET) as NodeList
|
||||
}
|
||||
|
||||
fun Node.print(encoding: Charset = Charsets.UTF_8) : String {
|
||||
trimTextNodes()
|
||||
return XmlUtils.nodeToString(this, encoding)
|
||||
/* previous implementation, without charset
|
||||
val domImplLS = document().implementation as DOMImplementationLS
|
||||
val serializer = domImplLS.createLSSerializer()
|
||||
return serializer.writeToString(this)
|
||||
*/
|
||||
}
|
||||
|
||||
fun Node.trimTextNodes() {
|
||||
val children: NodeList = getChildNodes()
|
||||
for (i in 0 until children.length) {
|
||||
val child = children.item(i)
|
||||
if (child.nodeType == Node.TEXT_NODE) {
|
||||
child.textContent = child.textContent.trim()
|
||||
}
|
||||
else child.trimTextNodes()
|
||||
}
|
||||
}
|
||||
|
||||
fun Node.prettyPrint(encoding: Charset = Charsets.UTF_8): String {
|
||||
trimTextNodes()
|
||||
return XmlUtils.nodeToPrettyString(this, encoding)
|
||||
}
|
||||
|
||||
|
||||
// node list iteration and random access
|
||||
|
||||
class NodeListIterator(private val lst: NodeList): Iterator<Node> {
|
||||
private var nextPos = 0
|
||||
override fun hasNext() = nextPos < lst.length
|
||||
override fun next() = lst.item(nextPos++)
|
||||
}
|
||||
|
||||
operator fun NodeList.iterator() = NodeListIterator(this)
|
||||
|
||||
operator fun NodeList.get(i: Int) = item(i)
|
||||
|
||||
// Encode XML entities in a string
|
||||
fun String.encodeXmlEntities() = StringEscapeUtils.escapeXml(this)
|
||||
|
@@ -0,0 +1,147 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
|
||||
import com.republicate.mailer.SmtpLoop
|
||||
import org.apache.commons.lang3.tuple.Pair
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.lang.IllegalAccessError
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.IllegalFormatCodePointException
|
||||
import javax.net.ssl.*
|
||||
import javax.servlet.*
|
||||
import javax.servlet.annotation.WebListener
|
||||
import javax.servlet.http.HttpSessionEvent
|
||||
import javax.servlet.http.HttpSessionListener
|
||||
|
||||
abstract class BaseWebappManager(val webappName: String, loggerName: String) : ServletContextListener, ServletContextAttributeListener, HttpSessionListener {
|
||||
|
||||
val logger = LoggerFactory.getLogger(loggerName)
|
||||
|
||||
protected fun disableSSLCertificateChecks() {
|
||||
// see http://www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate>? {
|
||||
return null
|
||||
}
|
||||
|
||||
@Suppress("TrustAllX509TrustManager")
|
||||
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
|
||||
@Suppress("TrustAllX509TrustManager")
|
||||
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
|
||||
}
|
||||
)
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
val sc = SSLContext.getInstance("SSL")
|
||||
sc.init(null, trustAllCerts, SecureRandom())
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
|
||||
|
||||
// Create all-trusting host name verifier
|
||||
val allHostsValid = HostnameVerifier { hostname, session -> true }
|
||||
|
||||
// Install the all-trusting host verifier
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid)
|
||||
} catch (e: Exception) {
|
||||
logger.error("could not disable SSL certificate checks", e)
|
||||
}
|
||||
}
|
||||
|
||||
private val webServices: MutableMap<String?, Pair<Runnable, Thread?>> = TreeMap()
|
||||
|
||||
@JvmOverloads
|
||||
fun registerService(name: String?, task: Runnable, initialStatus: Boolean? = null) {
|
||||
if (webServices.containsKey(name)) {
|
||||
logger.warn("service {} already registered")
|
||||
return
|
||||
}
|
||||
logger.debug("registered service {}", name)
|
||||
webServices[name] =
|
||||
Pair.of(task, null)
|
||||
}
|
||||
|
||||
fun startService(name: String?) {
|
||||
val service = webServices[name]!!
|
||||
if (service.right != null && service.right!!.isAlive) {
|
||||
logger.warn("service {} is already running", name)
|
||||
return
|
||||
}
|
||||
logger.debug("starting service {}", name)
|
||||
val thread = Thread(service.left, name)
|
||||
thread.start()
|
||||
webServices[name] =
|
||||
Pair.of(
|
||||
service.left,
|
||||
thread
|
||||
)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun stopService(name: String?, webappClosing: Boolean = false) {
|
||||
val service = webServices[name]!!
|
||||
val thread = service.right
|
||||
if (thread == null || !thread.isAlive) {
|
||||
logger.warn("service {} is already stopped", name)
|
||||
return
|
||||
}
|
||||
logger.debug("stopping service {}", name)
|
||||
thread.interrupt()
|
||||
try {
|
||||
thread.join()
|
||||
} catch (ie: InterruptedException) {
|
||||
}
|
||||
if (!webappClosing) {
|
||||
webServices[name] = Pair.of(service.left, null)
|
||||
}
|
||||
}
|
||||
|
||||
/* ServletContextListener interface */
|
||||
override fun contextInitialized(sce: ServletContextEvent) {
|
||||
context = sce.servletContext
|
||||
logger.info("---------- Starting $webappName ----------")
|
||||
webappRoot = context.getRealPath("/")
|
||||
try {
|
||||
// load default properties
|
||||
properties.load(context.getResourceAsStream("/WEB-INF/pairgoth.default.properties"))
|
||||
// override with system properties after stripping off the 'pairgoth.' prefix
|
||||
System.getProperties().filter { (key, value) -> key is String && key.startsWith(PAIRGOTH_PROPERTIES_PREFIX)
|
||||
}.forEach { (key, value) ->
|
||||
properties[(key as String).removePrefix(PAIRGOTH_PROPERTIES_PREFIX)] = value
|
||||
}
|
||||
|
||||
// set system user agent string to empty string
|
||||
System.setProperty("http.agent", "")
|
||||
|
||||
// disable (for now ?) the SSL certificate checks, because many sites
|
||||
// fail to correctly implement SSL...
|
||||
disableSSLCertificateChecks()
|
||||
|
||||
} catch (ioe: IOException) {
|
||||
logger.error("webapp initialization error", ioe)
|
||||
}
|
||||
}
|
||||
|
||||
override fun contextDestroyed(sce: ServletContextEvent) {
|
||||
logger.info("---------- Stopping $webappName ----------")
|
||||
for (service in webServices.keys) stopService(service, true)
|
||||
}
|
||||
|
||||
/* ServletContextAttributeListener interface */
|
||||
override fun attributeAdded(event: ServletContextAttributeEvent) {}
|
||||
override fun attributeRemoved(event: ServletContextAttributeEvent) {}
|
||||
override fun attributeReplaced(event: ServletContextAttributeEvent) {}
|
||||
|
||||
/* HttpSessionListener interface */
|
||||
override fun sessionCreated(se: HttpSessionEvent) {}
|
||||
override fun sessionDestroyed(se: HttpSessionEvent) {}
|
||||
|
||||
companion object {
|
||||
const val PAIRGOTH_PROPERTIES_PREFIX = "pairgoth."
|
||||
lateinit var webappRoot: String
|
||||
lateinit var context: ServletContext
|
||||
val properties = Properties()
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.util.Colorizer.blue
|
||||
import org.jeudego.pairgoth.util.Colorizer.green
|
||||
import org.jeudego.pairgoth.util.toString
|
||||
import org.slf4j.Logger
|
||||
import java.io.StringWriter
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
fun Logger.logRequest(req: HttpServletRequest, logHeaders: Boolean = false) {
|
||||
val builder = StringBuilder()
|
||||
builder.append(req.method).append(' ')
|
||||
.append(req.scheme).append("://")
|
||||
.append(req.localName)
|
||||
val port = req.localPort
|
||||
if (port != 80) builder.append(':').append(port)
|
||||
/*
|
||||
if (!req.contextPath.isEmpty()) {
|
||||
builder.append(req.contextPath)
|
||||
}
|
||||
*/
|
||||
builder.append(req.requestURI)
|
||||
if (req.method == "GET") {
|
||||
val qs = req.queryString
|
||||
if (qs != null) builder.append('?').append(qs)
|
||||
}
|
||||
// builder.append(' ').append(req.getProtocol());
|
||||
info(blue("<< {}"), builder.toString())
|
||||
if (isTraceEnabled && logHeaders) {
|
||||
// CB TODO - should be bufferized and asynchronously written in synchronous chunks
|
||||
// so that header lines from parallel requests are not mixed up in the logs ;
|
||||
// synchronizing the whole request log is not desirable
|
||||
val headerNames = req.headerNames
|
||||
while (headerNames.hasMoreElements()) {
|
||||
val name = headerNames.nextElement()
|
||||
val value = req.getHeader(name)
|
||||
trace(blue("<< {}: {}"), name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Logger.logPayload(prefix: String?, payload: Json, upstream: Boolean) {
|
||||
val writer = StringWriter()
|
||||
//payload.toPrettyString(writer, "");
|
||||
payload.toString(writer)
|
||||
if (isTraceEnabled) {
|
||||
for (line in writer.toString().split("\n".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()) {
|
||||
trace(if (upstream) blue("{}{}") else green("{}{}"), prefix, line)
|
||||
}
|
||||
} else {
|
||||
var line = writer.toString()
|
||||
val pos = line.indexOf('\n')
|
||||
if (pos != -1) line = line.substring(0, pos)
|
||||
if (line.length > 50) line = line.substring(0, 50) + "..."
|
||||
debug(if (upstream) blue("{}{}") else green("{}{}"), prefix, line)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun HttpServletRequest.getRemoteAddress(): String? {
|
||||
var ip = getHeader("X-Forwarded-For")
|
||||
if (ip == null) {
|
||||
ip = remoteAddr
|
||||
} else {
|
||||
val comma = ip.indexOf(',')
|
||||
if (comma != -1) {
|
||||
ip = ip.substring(0, comma).trim { it <= ' ' } // keep the left-most IP address
|
||||
}
|
||||
}
|
||||
return ip
|
||||
}
|
Reference in New Issue
Block a user