Skip to content

Commit

Permalink
#297 Add proxy capability to load catalogs
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Sep 18, 2020
1 parent cf91163 commit b5f865b
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 19 deletions.
118 changes: 118 additions & 0 deletions app/org/thp/cortex/services/CustomWSAPI.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.thp.cortex.services

import scala.util.control.NonFatal

import javax.inject.{Inject, Singleton}
import play.api.inject.ApplicationLifecycle
import play.api.libs.ws._
import play.api.libs.ws.ahc.{AhcWSClient, AhcWSClientConfig, AhcWSClientConfigParser}
import play.api.{Configuration, Environment, Logger}

import akka.stream.Materializer
import com.typesafe.sslconfig.ssl.TrustStoreConfig

object CustomWSAPI {
private[CustomWSAPI] lazy val logger = Logger(getClass)

def parseWSConfig(config: Configuration): AhcWSClientConfig =
new AhcWSClientConfigParser(new WSConfigParser(config.underlying, getClass.getClassLoader).parse(), config.underlying, getClass.getClassLoader)
.parse()

def parseProxyConfig(config: Configuration): Option[WSProxyServer] =
config.getOptional[Configuration]("play.ws.proxy").map { proxyConfig
DefaultWSProxyServer(
proxyConfig.get[String]("host"),
proxyConfig.get[Int]("port"),
proxyConfig.getOptional[String]("protocol"),
proxyConfig.getOptional[String]("user"),
proxyConfig.getOptional[String]("password"),
proxyConfig.getOptional[String]("ntlmDomain"),
proxyConfig.getOptional[String]("encoding"),
proxyConfig.getOptional[Seq[String]]("nonProxyHosts")
)
}

def getWS(config: Configuration)(implicit mat: Materializer): AhcWSClient = {
val clientConfig = parseWSConfig(config)
val clientConfigWithTruststore = config.getOptional[String]("play.cert") match {
case Some(p)
logger.warn("""Use of "cert" parameter in configuration file is deprecated. Please use:
| ws.ssl {
| trustManager = {
| stores = [
| { type = "PEM", path = "/path/to/cacert.crt" },
| { type = "JKS", path = "/path/to/truststore.jks" }
| ]
| }
| }
""".stripMargin)
clientConfig.copy(
wsClientConfig = clientConfig
.wsClientConfig
.copy(
ssl = clientConfig
.wsClientConfig
.ssl
.withTrustManagerConfig(
clientConfig
.wsClientConfig
.ssl
.trustManagerConfig
.withTrustStoreConfigs(
clientConfig.wsClientConfig.ssl.trustManagerConfig.trustStoreConfigs :+ TrustStoreConfig(
filePath = Some(p),
data = None
)
)
)
)
)
case None clientConfig
}
AhcWSClient(clientConfigWithTruststore, None)
}

def getConfig(config: Configuration, path: String): Configuration =
Configuration(
config
.getOptional[Configuration](s"play.$path")
.getOrElse(Configuration.empty)
.underlying
.withFallback(config.getOptional[Configuration](path).getOrElse(Configuration.empty).underlying)
)
}

@Singleton
class CustomWSAPI(
ws: AhcWSClient,
val proxy: Option[WSProxyServer],
config: Configuration,
environment: Environment,
lifecycle: ApplicationLifecycle,
mat: Materializer
) extends WSClient {
private[CustomWSAPI] lazy val logger = Logger(getClass)

@Inject() def this(config: Configuration, environment: Environment, lifecycle: ApplicationLifecycle, mat: Materializer) =
this(CustomWSAPI.getWS(config)(mat), CustomWSAPI.parseProxyConfig(config), config, environment, lifecycle, mat)

override def close(): Unit = ws.close()

override def url(url: String): WSRequest = {
val req = ws.url(url)
proxy.fold(req)(req.withProxyServer)
}

override def underlying[T]: T = ws.underlying[T]

def withConfig(subConfig: Configuration): CustomWSAPI = {
logger.debug(s"Override WS configuration using $subConfig")
try {
new CustomWSAPI(Configuration(subConfig.underlying.atKey("play").withFallback(config.underlying)), environment, lifecycle, mat)
} catch {
case NonFatal(e)
logger.error(s"WSAPI configuration error, use default values", e)
this
}
}
}
3 changes: 1 addition & 2 deletions app/org/thp/cortex/services/WorkerSrv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import scala.io.Codec
import scala.util.{Failure, Success, Try}

import play.api.libs.json.{JsArray, JsObject, JsString, Json}
import play.api.libs.ws.WSClient
import play.api.{Configuration, Logger}

import akka.NotUsed
Expand Down Expand Up @@ -37,7 +36,7 @@ class WorkerSrv @Inject() (
updateSrv: UpdateSrv,
deleteSrv: DeleteSrv,
findSrv: FindSrv,
ws: WSClient,
ws: CustomWSAPI,
implicit val ec: ExecutionContext,
implicit val mat: Materializer
) {
Expand Down
2 changes: 0 additions & 2 deletions app/org/thp/cortex/services/mappers/GroupUserMapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import org.elastic4play.controllers.Fields
class GroupUserMapper(
loginAttrName: String,
nameAttrName: String,
rolesAttrName: Option[String],
groupAttrName: String,
organizationAttrName: Option[String],
defaultRoles: Seq[String],
Expand All @@ -29,7 +28,6 @@ class GroupUserMapper(
this(
configuration.getOptional[String]("auth.sso.attributes.login").getOrElse("login"),
configuration.getOptional[String]("auth.sso.attributes.name").getOrElse("name"),
configuration.getOptional[String]("auth.sso.attributes.roles"),
configuration.getOptional[String]("auth.sso.attributes.groups").getOrElse(""),
configuration.getOptional[String]("auth.sso.attributes.organization"),
configuration.getOptional[Seq[String]]("auth.sso.defaultRoles").getOrElse(Seq()),
Expand Down
38 changes: 23 additions & 15 deletions conf/application.sample
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ auth {
# URL of the authorization server
#clientId = "client-id"
#clientSecret = "client-secret"
#redirectUri = "https://my-thehive-instance.example/index.html#!/login"
#redirectUri = "https://my-cortex-instance.example/api/ssoLogin"
#responseType = "code"
#grantType = "authorization_code"

Expand All @@ -124,8 +124,6 @@ auth {
# The endpoint from which to obtain user details using the OAuth token, after successful login
#userUrl = "https://auth-site.com/api/User"
#scope = "openid profile"
# Type of authorization header
#authorizationHeader = "Bearer" # or token
}

# Single-Sign On
Expand All @@ -139,22 +137,16 @@ auth {
# Autologin user using SSO?
#autologin = false

# Attributes mappings
# Name of mapping class from user resource to backend user ('simple' or 'group')
#mapper = group
#attributes {
# login = "login"
# login = "user"
# name = "name"
# groups = "groups"
# roles = "roles" # list of roles, separated with comma
# organisation = "org"
# organization = "org"
#}

# Name of mapping class from user resource to backend user ('simple' or 'group')
#mapper = group
# Default roles for users with no groups mapped ("read", "analyze", "orgadmin")
#defaultRoles = []
# Default organization
#defaultOrganization = "MyOrga"

#defaultRoles = ["read"]
#defaultOrganization = "csirt"
#groups {
# # URL to retreive groups (leave empty if you are using OIDC)
# #url = "https://auth-site.com/api/Groups"
Expand All @@ -165,6 +157,16 @@ auth {
# reader-profile-name = ["read"]
# }
#}

#mapper = simple
#attributes {
# login = "user"
# name = "name"
# roles = "roles"
# organization = "org"
#}
#defaultRoles = ["read"]
#defaultOrganization = "csirt"
}
}

Expand Down Expand Up @@ -211,4 +213,10 @@ responder {
}
}

# Proxy configuration to retrieve catalogs
# play.ws.proxy {
# host = proxy.example.com
# port = 3128
# }

# It's the end my friend. Happy hunting!

0 comments on commit b5f865b

Please sign in to comment.