Pac4j Scala - Multiple SAML2 clients in secure action - scala

I have a pac4j Config with two SAML2Client:
val clients = new Clients(baseUrl + "/domain/callback", samlClient1, samlClient2)
val config = new Config(clients)
My question:
How do I specify the client I want to use in a secure action if both clients are SAML2Client?
def SAMLSecure: SecureAction[SAML2Profile, AnyContent, AuthenticatedRequest] =
Secure(
clients = "SAML2Client", // How to specify samlClient1 or samlClient2
authorizers = myAuthorizers,
matchers = myMatchers
)

Found a solution.
val samlClient1 = new SAML2Client()
val samlClient2 = new SAML2Client()
samlClient1.setName("SamlClient1")
samlClient2.setName("SamlClient2")
val clients = new Clients(baseUrl + "/domain/callback", samlClient1, samlClient2)
val config = new Config(clients)
And then you can define the SecureAction like this:
def SAMLSecure: SecureAction[SAML2Profile, AnyContent, AuthenticatedRequest] =
Secure(
clients = "SamlClient1", // Or "SamlClient2" or both "SamlClient1,SamlClient2"
authorizers = myAuthorizers,
matchers = myMatchers
)

Related

Lightbend ssl-config with a non-http TCP Server

I've found several resources that provide details on configuring ssl-config options within the application.conf file and I've identified how to access these configurations using AkkaSSLConfig.get(). I've seen that an https context can be created using a AkkaSSLConfig object as a parameter to ConnectionContext.https().
Is it possible to use this for non-http servers? Is the context returned somehow specific to http? I'm trying to take advantage of ssl-config but it isn't clear to me that it provides any advantages for non-http servers and I don't see any convenient way of building a context from the ssl-config definition, in which case it seems I may as well define the context manually.
Lastly, any examples of building the context for non-http servers are difficult to find. It seems the process may be the same as for http servers, but I'm finding that examples often include the use of classes/methods that have 'http' in the name. If anyone knows of a good example I'd be very appreciative.
import java.io.{File, FileInputStream}
import java.security.{KeyStore, SecureRandom}
import akka.actor.ActorSystem
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives.pathSingleSlash
import akka.http.scaladsl.{ConnectionContext, Http}
import akka.stream.{ActorMaterializer, TLSClientAuth}
import com.typesafe.sslconfig.akka.AkkaSSLConfig
import com.typesafe.sslconfig.ssl.{KeyManagerConfig, KeyManagerFactoryWrapper, KeyStoreConfig, SSLConfigFactory, SSLConfigSettings}
import javax.net.ssl.{SSLContext, TrustManagerFactory}
import scala.concurrent.{ExecutionContext, Future}
object Test extends App{
implicit val actorSystem: ActorSystem = ActorSystem("test")
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContext = actorSystem.dispatcher
val ksConfig: KeyStoreConfig = KeyStoreConfig.apply(data = None,
filePath = Some("/Users/mshaik/testApp/src/main/resources/keystore/localhost.p12")
).withPassword(Some("test"))
val kmConfig: KeyManagerConfig = KeyManagerConfig().withKeyStoreConfigs(List(ksConfig))
val sslConfigSettings: SSLConfigSettings = SSLConfigFactory.defaultConfig.withKeyManagerConfig(kmConfig)
val akkaSSLConfig: AkkaSSLConfig = AkkaSSLConfig.get(actorSystem).withSettings(sslConfigSettings)
val ks: KeyStore = KeyStore.getInstance("PKCS12")
ks.load(new FileInputStream(new File(ksConfig.filePath.get)), ksConfig.password.get.toCharArray)
val kmf: KeyManagerFactoryWrapper = akkaSSLConfig.buildKeyManagerFactory(sslConfigSettings)
kmf.init(ks, ksConfig.password.get.toCharArray)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(ks)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
val ctx: ConnectionContext = ConnectionContext.https(sslContext,
sslConfig = Some(akkaSSLConfig),
clientAuth = Some(TLSClientAuth.Want)
)
var bindingFuture: Future[ServerBinding] = _
Http().setDefaultServerHttpContext(ctx)
val route: Route = pathSingleSlash {
get {
complete(HttpResponse(StatusCodes.OK, entity = "Welcome to base path!"))
}
}
try{
bindingFuture = Http().bindAndHandle(route, "localhost", 8085, connectionContext = ctx)
println( s"Server online at https://localhost:8085/")
} catch {
case ex: Exception =>
println(this.getClass, ex.getMessage, ex)
materializer.shutdown()
actorSystem.terminate()
}
}
I believe the answer to my question is that there isn't much use in thoroughly configuring TLS options within ssl-config when creating a non-HTTP TLS connection.
Not a single example I found shows how to define keystore and truststore parameters within the config and then use those configurations to create the SSLContext object (all examples configure the keystore/truststore parameters manually, within the code). Ultimately I found it wasn't useful to use ssl-config for storing configurations. The only place I found it useful is to obtain the list of default ciphers and default protocols (and hence I still use it in my code).
For reference, below is what I ended up doing to configure the context and initial session structure and create the TCP server. This is very similar to other examples found within documentation as well as some responses here on SO. Some differences in this response: 1) This requires client certificates, 2) This is for a server (as opposed to a client), 3) This code shows how to use factory methods to create the TLS BidiFlow (note the Tcp().bindTls call) 4) This allows you to pass in the Flow that will handle the incoming communications.
object TcpServerBindTls extends StrictLogging {
def apply(hostInterface: String, tcpPort: Int, handler: Flow[ByteString, ByteString, NotUsed])(implicit system: ActorSystem, materializer: ActorMaterializer) = {
val sslContext = buildSSLContext
val firstSession = prepareFirstSession(sslContext)
val connections: Source[Tcp.IncomingConnection, Future[Tcp.ServerBinding]] = Tcp().bindTls(hostInterface, tcpPort, sslContext, firstSession)
connections runForeach { connection =>
logger.info(s"New connection: ${connection}")
connection.handleWith(handler)
}
}
def prepareFirstSession(sslContext: SSLContext)(implicit system: ActorSystem) = {
val sslConfig = AkkaSSLConfig.get(system);
val config = sslConfig.config;
val defaultParams = sslContext.getDefaultSSLParameters();
val defaultProtocols = defaultParams.getProtocols();
val defaultCiphers = defaultParams.getCipherSuites();
val clientAuth = TLSClientAuth.need
defaultParams.setProtocols(defaultProtocols)
defaultParams.setCipherSuites(defaultCiphers)
val firstSession = new TLSProtocol.NegotiateNewSession(None, None, None, None)
.withCipherSuites(defaultCiphers: _*)
.withProtocols(defaultProtocols: _*)
.withParameters(defaultParams)
firstSession
}
def buildSSLContext: SSLContext = {
val bufferedSource = io.Source.fromFile("/path/to/password/file")
val keyStorePassword = bufferedSource.getLines.mkString
bufferedSource.close
val keyStore = KeyStore.getInstance("PKCS12");
val keyStoreLocation = "/path/to/keystore/file/server.p12"
val keyStoreFIS = new FileInputStream(keyStoreLocation)
keyStore.load(keyStoreFIS, keyStorePassword.toCharArray())
val trustStore = KeyStore.getInstance("PKCS12");
val trustStoreLocation = settings.tls.keyStoreLocation;
val trustStoreFIS = new FileInputStream(keyStoreLocation)
trustStore.load(trustStoreFIS, keyStorePassword.toCharArray())
val kmf = KeyManagerFactory.getInstance("SunX509")
kmf.init(keyStore, keyStorePassword.toCharArray())
val tmf = TrustManagerFactory.getInstance("SunX509")
tmf.init(trustStore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom())
sslContext
}
}

Scala http client does not reuse connections

in Scala, I have an akka http client class with some local binding:
class AkkaConPoolingHttpClient(
override val timeout: Option[FiniteDuration] = None,
val localBinding: Option[InetSocketAddress] = None,
val userAgentHeader: Option[String] = None)(
implicit val config: HttpClient.Config,
val system: ActorSystem,
val materializer: Materializer)
extends AkkaHttpClient {
protected val http = Http()
override def dispatch(request: HttpRequest): Future[HttpResponse] = {
val effectivePort = request.uri.effectivePort
val connection =
http.outgoingConnection(
request.uri.authority.host.address(),
port = effectivePort,
localAddress = localBinding)
val preparedRequest = userAgentHeader match {
case Some(userAgent) => fixUri(request.withHeaders(request.headers ++ Seq(headers.`User-Agent`(userAgent))))
case None => fixUri(request)
}
Source.single(preparedRequest) via connection runWith Sink.head
}
object AkkaConPoolingHttpClient {
private def fixUri(request: HttpRequest): HttpRequest =
request.withUri(request.uri.toRelative)
}
and I'm trying to see if it reuses the connections and it seems it doesn't:
val connectionCount = new AtomicInteger()
val testServerFuture = Http().bind("127.0.0.1", 0).to {
Sink.foreach { incomingConnection =>
connectionCount.incrementAndGet()
incomingConnection.flow.join(Flow[HttpRequest].map(_ => HttpResponse())).run()
}
}.run()
val testServerPort = Await.result(testServerFuture, defaultExpectTimeout)
.localAddress.getPort
val address = "127.0.0.1"
val addr = Some(new InetSocketAddress(address, 0))
val client = new AkkaConPoolingHttpClient(localBinding = addr)
// Send some requests concurrently
val requests = List(
Get(s"http://127.0.0.1:$testServerPort/1"),
Get(s"http://127.0.0.1:$testServerPort/2"),
Get(s"http://127.0.0.1:$testServerPort/3"))
val responses = Await.result(
Future.sequence(requests.map(client.sendRequest)),
defaultExpectTimeout)
// Send some more requests -- the connections from before should be reused
Thread.sleep(500)
val responses2 = Await.result(
Future.sequence(requests.map(client.sendRequest)),
defaultExpectTimeout)
// Usually this is "3", occasionally "4".
connectionCount.get() must beLessThanOrEqualTo(4)
Unfortunately, the test fails, connectionCount.get() has 6 connections. Why isn't it reuse the connections? what's wrong with this code?
I also tried with:
val effectivePort = request.uri.effectivePort
val clientSettings = ClientConnectionSettings(system).withSocketOptions(SO.ReuseAddress(true) :: Nil)
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
Http().outgoingConnection(
request.uri.authority.host.address(),
port = effectivePort,
localAddress = localBinding,
settings = clientSettings
)
..................
Source.single(preparedRequest)
.via(connectionFlow)
.runWith(Sink.head)
But I still have 6 connections in my test...
Problem
The problem is rooted in the fact that you are creation a new connection for each request. The client code is actually quite clear:
Source.single(preparedRequest) via connection runWith Sink.head
Each request is being sent through a newly instantiated connection. This is due to a general design flaw where you are getting the address from the request:
val connection =
http.outgoingConnection(
request.uri.authority.host.address(), //address comes from request
port = effectivePort,
localAddress = localBinding)
It would be more efficient to establish the address once (ensuring a single Connection), and then each Request would just need the path.
Solution
To use a single connection you'll have to create a single Flow and send all of your requests through that, as described here.

Bad Request on AWS ElasticSearch

I'm trying to connect to an IAM controlled ElasticSearch domain, I've created a request, and signed it, and everything works fine for method GET, but on method POST I get a 400 Bad Request
This clearly has something to do with the payload. If I provide a payload empty string ("") it works apporpriately, but anything else results in a bad request.
What am I missing?
val url = s"https://$host/TEST/article/_search"
val serviceName = "es"
val regionName = "us-east-1"
val request = new DefaultRequest(serviceName)
val payload =
"""{"1":"1"}""".trim
val payloadBytes = payload.getBytes(StandardCharsets.UTF_8)
val payloadStream = new ByteArrayInputStream(payloadBytes)
request.setContent(payloadStream)
val endpointUri = URI.create(url)
request.setEndpoint(endpointUri)
request.setHttpMethod(HttpMethodName.POST)
val credProvider = new EnvironmentVariableCredentialsProvider
val credentials = credProvider.getCredentials
val signer = new AWS4Signer
signer.setRegionName(regionName)
signer.setServiceName(serviceName)
signer.sign(request, credentials)
val context = new ExecutionContext(true)
val clientConfiguration = new ClientConfiguration()
val client = new AmazonHttpClient(clientConfiguration)
val rh = new MyHttpResponseHandler
val eh = new MyErrorHandler
val response =
client.execute(request, rh , eh, context);
note: if you run into this problem, inspect the actual content of the response, it may be a result of a mismatch between the index and your query.
My problem was that the specific query I was using was inappropriate for the specified index, and that resulted in a 400

Playframework Scala dinamically setup routes for scalatest

I am trying to write some integration tests. What I want to achieve is to setup a couple of fake URLs to emulate third party services. I want to know if it is possible to set dynamically URL path for test. For example I have this code
In a base file for the test I have this
override lazy val port = 1234
val myappTestConf = Map (
"app.twilio.lookups" -> s"https://localhost:$port",
)
override lazy val port = 1234
implicit override lazy val app: FakeApplication =
FakeApplication(
additionalConfiguration = myappTestConf
)
and then in a more specific file I have this
val getLookupPhoneUrl = s"${phoneNumber}"
implicit override lazy val app: FakeApplication =
FakeApplication(
additionalConfiguration = myappTestConf,
withRoutes = {
case ("GET", `getLookupPhoneUrl`) => Action(testLookupPhone(_))
}
)
The problem that I have is that this code does not compile because in the second file the phoneNumber has not been setted up, but I would like to set up dinamically, is that possible?
Thank you
One of the members of the team solved this with regex. Here is the answer
on the test files
val GetLookupPhone = """/v1/PhoneNumbers/([0-9\.\-]+)""".r
var phone: String = _
implicit override lazy val app: FakeApplication =
FakeApplication(
additionalConfiguration = educatinaTestConf,
withRoutes = {
case ("GET", GetLookupPhone(phone)) => Action(testLookupPhone(_, phone))
}
)
So then the route could be accessed if the route match the regex.

How to use Java libraries asynchronously in a Scala Play 2.0 application?

I see in the Play 2.0 Scala doc for calling web services that the idiomatic approach is to use Scala's asynchronous mechanisms to call web services. So if I'm using Java libraries for, say, downloading images from S3 and uploading to Facebook and Twitter (restfb and twitter4j), does this make for a highly inefficient use of resources (what resources?) or does it not make much difference (or no difference at all)?
If it makes a difference, how would I go about making something like the following asynchronous? Is there a quick way, or would I have to write libraries from scratch?
Note this will be running on heroku, if that matters in this discussion.
def tweetJpeg = Action(parse.urlFormEncoded) { request =>
val form = request.body
val folder = form("folder").head
val mediaType = form("type").head
val photo = form("photo").head
val path = folder + "/" + mediaType + "/" + photo
val config = Play.current.configuration;
val awsAccessKey = config.getString("awsAccessKey").get
val awsSecretKey = config.getString("awsSecretKey").get
val awsBucket = config.getString("awsBucket").get
val awsCred = new BasicAWSCredentials(awsAccessKey, awsSecretKey)
val amazonS3Client = new AmazonS3Client(awsCred)
val obj = amazonS3Client.getObject(awsBucket, path)
val stream = obj.getObjectContent()
val twitterKey = config.getString("twitterKey").get
val twitterSecret = config.getString("twitterSecret").get
val token = form("token").head
val secret = form("secret").head
val tweet = form("tweet").head
val cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(twitterKey)
.setOAuthConsumerSecret(twitterSecret)
.setOAuthAccessToken(token)
.setOAuthAccessTokenSecret(secret)
val tf = new TwitterFactory(cb.build())
val twitter = tf.getInstance()
val status = new StatusUpdate(tweet)
status.media(photo, stream)
val twitResp = twitter.updateStatus(status)
Logger.info("Tweeted " + twitResp.getText())
Ok("Tweeted " + twitResp.getText())
}
def facebookJpeg = Action(parse.urlFormEncoded) { request =>
val form = request.body
val folder = form("folder").head
val mediaType = form("type").head
val photo = form("photo").head
val path = folder + "/" + mediaType + "/" + photo
val config = Play.current.configuration;
val awsAccessKey = config.getString("awsAccessKey").get
val awsSecretKey = config.getString("awsSecretKey").get
val awsBucket = config.getString("awsBucket").get
val awsCred = new BasicAWSCredentials(awsAccessKey, awsSecretKey)
val amazonS3Client = new AmazonS3Client(awsCred)
val obj = amazonS3Client.getObject(awsBucket, path)
val stream = obj.getObjectContent()
val token = form("token").head
val msg = form("msg").head
val facebookClient = new DefaultFacebookClient(token)
val fbClass = classOf[FacebookType]
val param = com.restfb.Parameter.`with`("message", msg)
val attachment = com.restfb.BinaryAttachment`with`(photo + ".png", stream)
val fbResp = facebookClient.publish("me/photos", fbClass, attachment, param)
Logger.info("Posted " + fbResp.toString())
Ok("Posted " + fbResp.toString())
}
My attempt at a guess:
Yes it's better to do things asynchronous; you're tying up threads if you do everything synchronously. Threads are memory hogs, so your server can only use so many; the more that are tied up waiting, the fewer requests your server can respond to.
No it's not a huge issue. With node.js (and Rails? Django?) it is a huge issue because there's only one thread and so it blocks your whole web server. A JVM server is multithreaded so you can still service new requests.
You can easily wrap the whole thing in a future, or do it more granularly, but that doesn't really buy you anything because you're calling the same methods, so you're just shifting the wait from one thread do another.
If those Java libraries offer asynchronous methods, you can wrap those in a future to get the real benefits of asynchrony <-how to do?. Otherwise yes you're looking at writing from the ground up.
Don't really know if running on heroku matters. Is one dyno == one simultaneous request?
I think it's best to do these requests asynchronously for two main reasons:
high latency (network calls)
failures
With Play, you should use the Akka actors to make your actions it provides great ways to deal with these two concerns.
The problem synchronous code is that it will block the web server. So it won't be available to other requests. Here we will make the wait in other threads unrelated to the web server.
You could do something like:
// you will have to write the TwitterActor
val twitterActor = Akka.system.actorOf(Props[TwitterActor], name = "twitter-actor")
def tweetJpeg = Action(parse.urlFormEncoded) { request =>
val futureMessage = (twitterActor ? request.body).map {
// Do something with the response from the actor
case ... => ...
}
async {
futureMessage.map( message =>
ok("Tweeted " + message)
)
}
}
Your actor would receive the body and send back the response of the service.
Moreover with Akka, you can tune your process to have several actors available, have a circuit breaker ...
To go further: http://doc.akka.io/docs/akka/2.1.2/scala/actors.html
Ps: I never tried play on Heroku so I don't know the impact of a single dynamo.