OAuth in progress
This commit is contained in:
15
oauth.sh
Executable file
15
oauth.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap 'kill $CSSWATCH; exit' INT
|
||||
( cd view-webapp; ./csswatch.sh ) &
|
||||
CSSWATCH=$!
|
||||
|
||||
export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5006"
|
||||
#mvn --projects view-webapp -Dpairgoth.api.url=http://localhost:8085/api/ package jetty:run
|
||||
mvn -DskipTests=true \
|
||||
-Dpairgoth.auth=oauth \
|
||||
-Dpairgoth.oauth.providers=ffg \
|
||||
-Dpairgoth.oauth.ffg.secret=43f3a67bffcb5054d2f1b0e2a2374bdc \
|
||||
-Dwebapp.external.url=http://localhost:8080
|
||||
--projects view-webapp package jetty:run
|
||||
kill $CSSWATCH
|
3
pom.xml
3
pom.xml
@@ -80,7 +80,8 @@
|
||||
<pairgoth.store>file</pairgoth.store>
|
||||
<pairgoth.store.file.path>tournamentfiles</pairgoth.store.file.path>
|
||||
<pairgoth.auth>none</pairgoth.auth>
|
||||
<pairgoth.auth.sesame></pairgoth.auth.sesame>
|
||||
<pairgoth.auth.sesame>this_should_be_overriden_with_a_command_line_option</pairgoth.auth.sesame>
|
||||
<pairgoth.oauth.ffg.client_id>pairtogh</pairgoth.oauth.ffg.client_id>
|
||||
<pairgoth.smtp.sender></pairgoth.smtp.sender>
|
||||
<pairgoth.smtp.host></pairgoth.smtp.host>
|
||||
<pairgoth.smtp.port>587</pairgoth.smtp.port>
|
||||
|
@@ -232,9 +232,20 @@
|
||||
-->
|
||||
<!-- http client -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</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.14</version>
|
||||
</dependency>
|
||||
<!-- server-side events -->
|
||||
<dependency>
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package org.jeudego.pairgoth.oauth
|
||||
|
||||
class FFGHelper : OAuthHelper() {
|
||||
override val name: String
|
||||
get() = "facebook"
|
||||
|
||||
private val FFG_HOST = "https://testffg"
|
||||
|
||||
override fun getLoginURL(sessionId: String?): String {
|
||||
return "$FFG_HOST/oauth2/entry_point.php/authorize?" +
|
||||
"client_id=" + clientId +
|
||||
"&redirect_uri=" + redirectURI +
|
||||
"&scope=email"
|
||||
}
|
||||
|
||||
override fun getAccessTokenURL(code: String): String? {
|
||||
return "$FFG_HOST/oauth2/entry_point.php/access_token?" +
|
||||
"client_id=" + clientId +
|
||||
"&redirect_uri=" + redirectURI +
|
||||
"&client_secret=" + secret +
|
||||
"&code=" + code
|
||||
}
|
||||
|
||||
override fun getUserInfosURL(accessToken: String): String? {
|
||||
return "$FFG_HOST/oauth2/entry_point.php/user_info?" +
|
||||
"field=email" +
|
||||
"&access_token=" + accessToken
|
||||
}
|
||||
}
|
@@ -7,6 +7,10 @@ import org.jeudego.pairgoth.web.WebappManager
|
||||
//import com.republicate.modality.util.AESCryptograph
|
||||
//import com.republicate.modality.util.Cryptograph
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
import org.jeudego.pairgoth.util.AESCryptograph
|
||||
import org.jeudego.pairgoth.util.ApiClient.JsonApiClient
|
||||
import org.jeudego.pairgoth.util.Cryptograph
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.io.UnsupportedEncodingException
|
||||
@@ -21,7 +25,7 @@ abstract class OAuthHelper {
|
||||
protected get() = WebappManager.getMandatoryProperty("oauth." + name + ".secret")
|
||||
protected val redirectURI: String?
|
||||
protected get() = try {
|
||||
val uri: String = WebappManager.Companion.getProperty("webapp.external.url") + "/oauth.html"
|
||||
val uri: String = WebappManager.getMandatoryProperty("webapp.external.url") + "/oauth"
|
||||
URLEncoder.encode(uri, "UTF-8")
|
||||
} catch (uee: UnsupportedEncodingException) {
|
||||
logger.error("could not encode redirect URI", uee)
|
||||
@@ -48,30 +52,23 @@ abstract class OAuthHelper {
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getUserEmail(accessToken: String): String {
|
||||
val json: Json.Object = Json.Object()
|
||||
// TODO
|
||||
// apiClient.get(getUserInfosURL(accessToken))
|
||||
return json.getString("email") ?: throw IOException("could not fetch email")
|
||||
val json = getUserInfosURL(accessToken)?.let { JsonApiClient.get(it).asObject() }
|
||||
return json?.getString("email") ?: throw IOException("could not fetch email")
|
||||
}
|
||||
|
||||
companion object {
|
||||
protected var logger = LoggerFactory.getLogger("oauth")
|
||||
protected var logger: Logger = LoggerFactory.getLogger("oauth")
|
||||
private const val salt = "0efd28fb53cbac42"
|
||||
// private val sessionIdCrypto: Cryptograph = AESCryptograph().apply {
|
||||
// init(salt)
|
||||
// }
|
||||
private val sessionIdCrypto: Cryptograph = AESCryptograph().apply {
|
||||
init(salt)
|
||||
}
|
||||
|
||||
private fun encrypt(input: String): String {
|
||||
return "TODO"
|
||||
// return Base64.encodeBase64URLSafeString(sessionIdCrypto.encrypt(input))
|
||||
return Base64.encodeBase64URLSafeString(sessionIdCrypto.encrypt(input))
|
||||
}
|
||||
|
||||
private fun decrypt(input: String): String {
|
||||
return "TODO"
|
||||
// return sessionIdCrypto.decrypt(Base64.decodeBase64(input))
|
||||
return sessionIdCrypto.decrypt(Base64.decodeBase64(input))
|
||||
}
|
||||
|
||||
// TODO
|
||||
// private val apiClient: ApiClient = ApiClient()
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package org.jeudego.pairgoth.oauth
|
||||
|
||||
object OauthHelperFactory {
|
||||
private val ffg: OAuthHelper = FFGHelper()
|
||||
private val facebook: OAuthHelper = FacebookHelper()
|
||||
private val google: OAuthHelper = GoogleHelper()
|
||||
private val instagram: OAuthHelper = InstagramHelper()
|
||||
|
@@ -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,341 @@
|
||||
package org.jeudego.pairgoth.util.ApiClient
|
||||
|
||||
import org.jeudego.pairgoth.util.parse
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.apache.http.*
|
||||
import org.apache.http.client.ClientProtocolException
|
||||
import org.apache.http.client.config.CookieSpecs
|
||||
import org.apache.http.client.config.RequestConfig
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity
|
||||
import org.apache.http.client.methods.*
|
||||
import org.apache.http.config.SocketConfig
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory
|
||||
import org.apache.http.entity.ContentType
|
||||
import org.apache.http.entity.EntityTemplate
|
||||
import org.apache.http.entity.StringEntity
|
||||
import org.apache.http.entity.mime.HttpMultipartMode
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder
|
||||
import org.apache.http.entity.mime.content.StringBody
|
||||
import org.apache.http.impl.client.HttpClients
|
||||
import org.apache.http.message.BasicHeader
|
||||
import org.apache.http.message.BasicNameValuePair
|
||||
import org.apache.http.ssl.SSLContexts
|
||||
import org.apache.http.util.EntityUtils
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.w3c.dom.Element
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.BufferedReader
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStream
|
||||
import java.io.StringReader
|
||||
import java.net.ProtocolException
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
|
||||
/**
|
||||
* This class implements a basic API client around Apache HTTP client.
|
||||
*/
|
||||
|
||||
val API_CLIENT_TIMEOUT = 60000
|
||||
|
||||
// TODO cookieStore ? credentialsProvider ?
|
||||
// CookieStore cookieStore = new BasicCookieStore();
|
||||
// CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||
|
||||
private val client = HttpClients.custom()
|
||||
.setSSLSocketFactory(
|
||||
SSLConnectionSocketFactory(
|
||||
SSLContexts.createSystemDefault(), arrayOf("TLSv1.2"),
|
||||
null,
|
||||
SSLConnectionSocketFactory.getDefaultHostnameVerifier()
|
||||
)
|
||||
)
|
||||
.setConnectionTimeToLive(1, TimeUnit.MINUTES)
|
||||
.setDefaultSocketConfig(
|
||||
SocketConfig.custom()
|
||||
.setSoTimeout(API_CLIENT_TIMEOUT)
|
||||
.build()
|
||||
)
|
||||
.setDefaultRequestConfig(
|
||||
RequestConfig.custom()
|
||||
.setConnectTimeout(API_CLIENT_TIMEOUT)
|
||||
.setSocketTimeout(API_CLIENT_TIMEOUT)
|
||||
.setCookieSpec(CookieSpecs.STANDARD)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
// CB TODO - this should go elsewhere
|
||||
fun ByteArray.reader(charset: Charset) =
|
||||
BufferedReader(InputStreamReader(ByteArrayInputStream(this), charset))
|
||||
|
||||
fun header(name: String, value: String) = BasicHeader(name, value)
|
||||
|
||||
fun param(name: String, value: String) = BasicNameValuePair(name, value)
|
||||
|
||||
// Incomplete list of binary content types, that we should avoid to log
|
||||
fun ContentType.isBinary() = mimeType.startsWith("image/") || mimeType.endsWith("pdf")
|
||||
fun ContentType.isText() = !isBinary()
|
||||
|
||||
data class BinaryData(val contentType: ContentType, val data: ByteArray, val name: String, val filename: String? = null)
|
||||
|
||||
fun buildMultiPartBody(payload: Json.Object, binaryData: BinaryData? = null): HttpEntity {
|
||||
val builder = MultipartEntityBuilder.create()
|
||||
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
for (entry in payload.entries) {
|
||||
builder.addPart(entry.key, StringBody(entry.value.toString(), ContentType.MULTIPART_FORM_DATA))
|
||||
}
|
||||
binaryData?.let {
|
||||
builder.addBinaryBody(it.name, it.data, it.contentType, it.filename ?: "file")
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
abstract class BaseApiClient<T> {
|
||||
|
||||
companion object {
|
||||
internal var logger = LoggerFactory.getLogger("api")
|
||||
}
|
||||
|
||||
private fun <B> build(method: String, url: String, body: B? = null, vararg keyValues: NameValuePair): HttpRequestBase {
|
||||
|
||||
val headers = mutableListOf<Header>()
|
||||
val params = mutableListOf<NameValuePair>()
|
||||
for (pair in keyValues) {
|
||||
when (pair) {
|
||||
is Header -> headers.add(pair)
|
||||
else -> params.add(pair)
|
||||
}
|
||||
}
|
||||
|
||||
val req: HttpRequestBase = when (method) {
|
||||
"GET" -> {
|
||||
val paramsString = params.map { "${it.name}=${it.value}" }.joinToString("&")
|
||||
val finalURL = url + (if (url.contains('?')) '&' else '?' ) + paramsString
|
||||
HttpGet(finalURL)
|
||||
}
|
||||
"POST" -> {
|
||||
HttpPost(url).also { req ->
|
||||
if (params.isNotEmpty() && body != null) {
|
||||
throw ClientProtocolException("specify POST body or POST parameters but not both")
|
||||
}
|
||||
if (params.isNotEmpty()) {
|
||||
val entity = UrlEncodedFormEntity(params, "UTF-8")
|
||||
req.entity = entity
|
||||
} else if (body != null) {
|
||||
val entity = if (body is HttpEntity) body
|
||||
else StringEntity(body.toString(),
|
||||
if (body is Json) ContentType.APPLICATION_JSON
|
||||
else ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8))
|
||||
req.entity = entity
|
||||
}
|
||||
}
|
||||
}
|
||||
"PATCH" -> {
|
||||
HttpPatch(url).also { req ->
|
||||
if (params.isNotEmpty() && body != null) {
|
||||
throw ClientProtocolException("specify PATCH body or PATCH parameters but not both")
|
||||
}
|
||||
if (params.isNotEmpty()) {
|
||||
val entity = UrlEncodedFormEntity(params, "UTF-8")
|
||||
req.entity = entity
|
||||
} else if (body != null) {
|
||||
val entity = if (body is HttpEntity) body
|
||||
else EntityTemplate { outputstream: OutputStream ->
|
||||
outputstream.write(body.toString().toByteArray())
|
||||
outputstream.flush()
|
||||
}.also {
|
||||
it.setContentType(ContentType.APPLICATION_JSON.toString())
|
||||
}
|
||||
req.entity = entity
|
||||
}
|
||||
}
|
||||
}
|
||||
"DELETE" -> {
|
||||
HttpDelete(url).also { req ->
|
||||
if (params.isNotEmpty() || body != null) {
|
||||
throw ClientProtocolException("DELETE body or DELETE parameters not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw ClientProtocolException("unhandled method: $method")
|
||||
}
|
||||
headers.addAll(acceptHeaders())
|
||||
headers.forEach {
|
||||
req.addHeader(it)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
private fun submit(req: HttpRequestBase): Pair<ByteArray, ContentType> {
|
||||
try {
|
||||
val resp: HttpResponse = client.execute(req)
|
||||
val statusLine = resp.statusLine
|
||||
val status = statusLine.statusCode
|
||||
when {
|
||||
status in 200..299 -> {
|
||||
if (status != 204 && resp.entity == null) throw ClientProtocolException("Response is empty")
|
||||
val body = resp.entity?.let { EntityUtils.toByteArray(it) }
|
||||
val contentType = resp.entity?.let { ContentType.get(it) }
|
||||
if (logger.isTraceEnabled) traceResponse(resp, body?.toString(contentType?.charset ?: StandardCharsets.UTF_8), contentType)
|
||||
return Pair(body ?: "{}".toByteArray(StandardCharsets.UTF_8), contentType ?: ContentType.APPLICATION_JSON.withCharset(StandardCharsets.UTF_8))
|
||||
}
|
||||
else -> {
|
||||
val body = resp.entity?.let { EntityUtils.toString(it) }
|
||||
val contentType = resp.entity?.let { ContentType.get(it) }
|
||||
if (logger.isTraceEnabled) traceResponse(resp, body, contentType)
|
||||
var message = statusLine.toString()
|
||||
if (body != null) message = "$message - $body"
|
||||
throw IOException(message)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
req.releaseConnection()
|
||||
}
|
||||
}
|
||||
|
||||
private fun traceRequest(req: HttpRequestBase, body: String? = null) {
|
||||
logger.trace(">> ${req.method} ${req.uri}")
|
||||
for (header in req.allHeaders) {
|
||||
logger.trace(">> $header")
|
||||
}
|
||||
if (body != null) {
|
||||
val contentType = req.allHeaders.firstOrNull { it.name == HttpHeaders.CONTENT_TYPE }?.let {
|
||||
ContentType.parse(it.value)
|
||||
}
|
||||
if (contentType?.isText() ?: true) {
|
||||
for (line in body.split(Regex("[\r\n]"))) {
|
||||
logger.trace(">> $line")
|
||||
}
|
||||
} else {
|
||||
logger.trace(">> ${contentType?.toString() ?: "unknown mime type"}, ${body.length} bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun traceResponse(resp: HttpResponse, body: String?, contentType: ContentType?) {
|
||||
val statusLine = resp.statusLine
|
||||
logger.trace("<< ${statusLine.statusCode} ${statusLine.reasonPhrase}")
|
||||
for (header in resp.allHeaders) logger.trace("<< $header")
|
||||
if (body != null) {
|
||||
val knownContentType = contentType ?: resp.allHeaders.firstOrNull { it.name == HttpHeaders.CONTENT_TYPE }?.let {
|
||||
ContentType.parse(it.value)
|
||||
}
|
||||
if (knownContentType?.isText() ?: true) {
|
||||
for (line in body.split(Regex("[\r\n]"))) {
|
||||
logger.trace("<< $line")
|
||||
}
|
||||
} else {
|
||||
logger.trace("<< ${contentType?.toString() ?: "unknown mime type"}, ${body.length} bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun acceptHeaders(): List<Header>
|
||||
|
||||
protected abstract fun parseResult(result: Pair<ByteArray, ContentType>): T
|
||||
|
||||
fun get(url: String, vararg with: NameValuePair): T {
|
||||
val req = build("GET", url, null, *with)
|
||||
if (logger.isTraceEnabled) traceRequest(req)
|
||||
val result = submit(req)
|
||||
return parseResult(result)
|
||||
}
|
||||
|
||||
fun <B> post(url: String, body: B?, vararg with: NameValuePair): T {
|
||||
val req = build("POST", url, body, *with)
|
||||
if (logger.isTraceEnabled) traceRequest(req, body?.toString())
|
||||
val result = submit(req)
|
||||
return parseResult(result)
|
||||
}
|
||||
|
||||
fun <B> patch(url: String, body: B?, vararg with: NameValuePair): T {
|
||||
val req = build("PATCH", url, body, *with)
|
||||
if (logger.isTraceEnabled) traceRequest(req, body?.toString())
|
||||
val result = submit(req)
|
||||
return parseResult(result)
|
||||
}
|
||||
|
||||
fun <B> delete(url: String, body: B?, vararg with: NameValuePair): T {
|
||||
val req = build("DELETE", url, body, *with)
|
||||
if (logger.isTraceEnabled) traceRequest(req, body?.toString())
|
||||
val result = submit(req)
|
||||
return parseResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
object AgnosticApiClient: BaseApiClient<Pair<ByteArray, ContentType>>() {
|
||||
override fun acceptHeaders() = listOf(BasicHeader(HttpHeaders.ACCEPT, "*/*"))
|
||||
override fun parseResult(result: Pair<ByteArray, ContentType>) = result
|
||||
}
|
||||
|
||||
object JsonApiClient: BaseApiClient<Json>() {
|
||||
|
||||
override fun acceptHeaders(): List<Header> {
|
||||
return listOf(BasicHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.mimeType))
|
||||
}
|
||||
|
||||
override fun parseResult(result: Pair<ByteArray, ContentType>): Json {
|
||||
when (result.second.mimeType) {
|
||||
ContentType.APPLICATION_JSON.mimeType, "application/vnd.api+json" -> {
|
||||
return Json.parse(result.first.reader(result.second.charset ?: StandardCharsets.UTF_8))!!
|
||||
}
|
||||
ContentType.APPLICATION_FORM_URLENCODED.mimeType -> {
|
||||
val json = Json.MutableObject()
|
||||
val charset = result.second.charset ?: StandardCharsets.UTF_8
|
||||
val decoded = URLDecoder.decode(result.first.toString(charset), charset.name())
|
||||
decoded.split("&").forEach {
|
||||
val keyValue = it.split(Regex("="), 2)
|
||||
if (keyValue.size != 2) throw ProtocolException("expecting a key-value pair: $it")
|
||||
val prev = json.put(keyValue[0], keyValue[1])
|
||||
if (prev != null) throw ClientProtocolException("Unsupported redundant values in response for key: " + keyValue[0]);
|
||||
}
|
||||
return json
|
||||
}
|
||||
else -> throw ClientProtocolException("invalid content type: ${result.second.mimeType}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CB TODO - needs to factorize XmlUtils with server => needs a common module
|
||||
//
|
||||
//object XmlApiClient: BaseApiClient<Element>() {
|
||||
//
|
||||
// override fun acceptHeaders(): List<Header> {
|
||||
// return listOf(
|
||||
// BasicHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_XML.mimeType),
|
||||
// BasicHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_SOAP_XML.mimeType)
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override fun parseResult(result: Pair<ByteArray, ContentType>): Element {
|
||||
// when (result.second.mimeType) {
|
||||
// ContentType.APPLICATION_XML.mimeType, ContentType.APPLICATION_SOAP_XML.mimeType -> {
|
||||
// return XmlUtils.parse(result.first.reader(result.second.charset ?: StandardCharsets.UTF_8)) ?: throw ClientProtocolException("empty XML body")
|
||||
// }
|
||||
// else -> throw ClientProtocolException("invalid content type: ${result.second.mimeType}")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//object SoapTextApiClient: BaseApiClient<Element>() {
|
||||
//
|
||||
// override fun acceptHeaders() = listOf(BasicHeader(HttpHeaders.ACCEPT, "*/*"))
|
||||
//
|
||||
// override fun parseResult(result: Pair<ByteArray, ContentType>): Element {
|
||||
// when (result.second.mimeType) {
|
||||
// ContentType.TEXT_XML.mimeType -> {
|
||||
// return XmlUtils.parse(result.first.reader(result.second.charset ?: StandardCharsets.UTF_8)) ?: throw ClientProtocolException("empty XML body")
|
||||
// }
|
||||
// else -> throw ClientProtocolException("invalid content type: ${result.second.mimeType}")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
@@ -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
|
||||
}
|
@@ -67,8 +67,14 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
||||
logger.info("pairgoth server ${properties["version"]} with profile ${properties["env"]}")
|
||||
|
||||
// publish some properties to the webapp context; for easy access from the template
|
||||
context.setAttribute("env", properties["env"])
|
||||
context.setAttribute("version", properties["version"] ?: "?")
|
||||
context.setAttribute("env", properties.getProperty("env") ?: "dev")
|
||||
context.setAttribute("version", properties.getProperty("version") ?: "?")
|
||||
properties.get("auth")?.let {
|
||||
|
||||
}
|
||||
properties.getProperty("oauth.providers")?.let {
|
||||
context.setAttribute("oauth.providers", it.split(Regex("\\s*,\\s*")))
|
||||
}
|
||||
|
||||
// set system user agent string to empty string
|
||||
System.setProperty("http.agent", "")
|
||||
|
Reference in New Issue
Block a user