How can I restrict access to list of IP in Play Framework using Scala?
I'm using Play Framework 2.2.4
I found solution for Java:
http://feadro.com/simple-ip-access-list-for-play-2-1-with-java/
How should I do it in Scala?
Stick the IPs you want to restrict to in the application.conf.
myapp.ipwhitelist = ["192.168.1.1", ...]
Then make a global filter which is applied to every incoming request, something like:
import scala.collection.JavaConverters._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.iteratee.Iteratee
import play.api.Play.current
import play.api.mvc._
object IPFilter extends EssentialFilter {
def apply(nextFilter: EssentialAction) = new EssentialAction {
def apply(requestHeader: RequestHeader) = {
// read the IPs as a Scala Seq (converting from the Java list)
val ips: Seq[String] = current.configuration.getStringList("myapp.ipwhitelist")
.map(_.asScala).getOrElse(Seq.empty)
// Check we've got an allowed IP, otherwise ignore the
// request body and immediately return a forbidden.
if (ips.contains(requestHeader.remoteAddress)) nextFilter(requestHeader)
else Iteratee.ignore[Array[Byte]]
.map(_ => Results.Forbidden(s"Bad IP! ${requestHeader.remoteAddress}"))
}
}
}
Then enable that in your application Global object:
object Global extends WithFilters(IPFilter) with GlobalSettings
If you want more flexibility you can use the same logic but with Action composition instead of a global filter.
I think that for production HTTP environment requiring more than simple configuration (public, accepting any HTTP request), an HTTP frontend service should be used to dispatch appropriately request to Play.
As for Can I (DNS) map one subdomain to multiple Play Framework entry points , there is advantage of using a service such Apache, Nginx or Varnish to configure HTTP ACL (Access Control List). For example in Varnish:
acl my-acl {
"an-authorized-host";
"1.2.3.4";
}
# Then ...
if (!client.ip ~ my-acl) {
error 405 "Not allowed.";
}
Related
I am not sure on what is the best approach and how to set the ETAG value for a JSON resource when using the Scala Playframework.
My use case is as follows: I have a service which will GET the resource and display it in the user's browser. On any subsequent PUT action, I want to check if that resource has changed in the backend.
My question is when I get the JSON resource, what is the idiomatic way to calculate the hash and set the etag value for the JSON representation that is served. Is it simply retrieve the JSON resource, calculate the hash, set the header and serve the resource or does Play provide any utilities to do this or is there a sbt plugin which can do this?
Play provides Cached class to add caching to an Action. Here is example usage:
package controllers
import javax.inject._
import play.api.cache.{Cached, CachedBuilder}
import play.api.mvc._
import scala.concurrent.duration._
#Singleton
class HomeController #Inject()(cc: ControllerComponents, cached: Cached)(implicit assetsFinder: AssetsFinder)
extends AbstractController(cc) {
val cacheOk: CachedBuilder =
cached.status((req: RequestHeader) => "someKey", OK, 5.minutes)
def index: EssentialAction = cacheOk { Action {
Ok(views.html.index("Your new application is ready."))
}
}
}
Cached will add ETag and handle If-None-Match. However, it sets the hash of expiration date as ETag:
val etag = s""""${Codecs.sha1(expirationDate)}""""
val resultWithHeaders = result.withHeaders(ETAG -> etag, EXPIRES -> expirationDate)
I am not aware Play provides out-of-the-box ETag as hash of JSON, however it seems possible to implement a similar class on the basis of Cached with handleResult modified to hash JSON body of Result.
I am deploying a Finatra app to Heroku. Thanks to Twitter guys together with Heroku this is a very easy task. The thing is that Heorku gives you https out of the box (if im trying to reach my service through https it just works). Nevertheless it also works with http requests. Is there any way to disable http requests and leave only https?
Thanks
You can disable the http request by override the defaultHttpPort value to an empty String (and do not pass a value for the -http.port flag)
import com.twitter.finagle.Http
import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter
object ExampleHttpsServerMain extends ExampleHttpsServer
class ExampleHttpsServer
extends HttpServer
with Tls {
override val defaultHttpPort: String = "" // disable the default HTTP port
override val defaultHttpsPort: String = ":443"
override def configureHttp(router: HttpRouter): Unit = {
router
.add[ExampleController]
}
}
In my play framework 2 application I'd like to have a log message with the request, response, and some details about the response - such as the number of search results returned from an external web call.
What I have now is a filter like this:
object AccessLog extends Filter {
import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits._
def apply(next: RequestHeader => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
val result = next(request)
result map { r =>
play.Logger.info(s"Request: ${request.uri} - Response: ${r.header.status}")
}
result
}
}
At the point of logging, I've alread converted my classes into json, so it seems wasteful to parse the json back into objects so I can log information about it.
Is it possible to compute the number of search results earlier in the request pipeline, maybe into a dictionary, and pull them out when I log the message here?
I was looking at flash, but don't want the values to be sent out in a cookie at any cost. Maybe I can clear the flash instead. Buf if there's a more suitable way I'd like to see that.
This is part of a read-only API that does not involve user accounts or sessions.
You could try using the play.api.cache.Cache object if you can come up with a reproducible unique request identifier. Once you have logged your request, you can remove it from the Cache.
My colleague and I have been puzzled with the different behaviour the DistributedPubSubMediator has for subscribing/unsubscribing directly or via a proxy Actor. We put together a test to show the different result below.
From our understanding, the ActorRef.forward should pass in the original sender, hence whether the message is sent directly to the Mediator or via a proxy Actor should not matter. Ie. http://www.scala-lang.org/api/current/index.html#scala.actors.ActorRef.
To work around, we have to extend the DIstributedPubSubMediator class and include the logic DistributedPubSubMediator object already provides. Ideally, we'd prefer to use the object directly and revert our code.
This seems like a bug. Does anyone know the underlying reason for this unusual behaviour? Please help...
[22-Oct-2013] Test is updated based on Roland's answer (Thank you) and added expectMsgType on SubscriberAck and UnsubscribeAck. We now receive the SubscribeAck, but strangely not the UnSubscribeAck. It is not a major issue but we would like to know why.
Another question, if we may ask altogether, is whether it is good practice to Subscribe remote actors to the DistributedPubSubMediator via proxy Actor running in the same ActorSystem?
At the moment we have:
The subscribing App discovers the publishing App (in non-Akka-way) and gets Cluster address.
The remote subscriber uses this address and the known proxy actor's path to send Identity request.
The remote subscriber gets the ActorIdentity response and then Subscribes/Unsubscribes via this (remote) proxy.
On the publisher App Subscribe/Unsubscribe messages are forwarded to the DistributedPubSubMediator and it is used to publish subsequent business messages.
We are not joining the Cluster as per Akka Reactor pubsub chat client example (ie. only using the DistributedPubSubMediator to publish) because we need to handle Failover on the Publisher side.
[5-Nov-2013] Added a test on Send message. It does not seem to work and we haven't figured it out yet.
package star.common.pubsub
import org.scalatest.{BeforeAndAfterAll, FunSuite}
import org.junit.runner.RunWith
import akka.contrib.pattern.DistributedPubSubExtension
import akka.contrib.pattern.DistributedPubSubMediator._
import akka.testkit.TestKit
import akka.actor.{Actor, ActorSystem, ActorRef, Props}
import scala.concurrent.duration._
import com.typesafe.config.ConfigFactory
object MediatorTest {
val config = ConfigFactory.parseString(s"""
akka.actor.provider="akka.cluster.ClusterActorRefProvider"
akka.remote.netty.tcp.port=0
akka.extensions = ["akka.contrib.pattern.DistributedPubSubExtension"]
""")
}
#RunWith(classOf[org.scalatest.junit.JUnitRunner])
class MediatorTest extends TestKit(ActorSystem("test", MediatorTest.config)) with FunSuite {
val mediator = DistributedPubSubExtension(system).mediator
val topic = "example"
val message = "Published Message"
// val joinAddress = Cluster(system).selfAddress
// Cluster(system).join(joinAddress)
test("Direct subscribe to mediator") {
mediator.!(Subscribe(topic, testActor))(testActor)
expectMsgType[SubscribeAck](5 seconds)
mediator.!(Publish(topic, message))(testActor)
expectMsg(2 seconds, message)
mediator.!(Unsubscribe(topic, testActor))(testActor)
expectMsgType[UnsubscribeAck](5 seconds)
mediator ! Publish(topic, message)
expectNoMsg(2 seconds)
}
test("Subscribe to mediator via proxy") {
class Proxy extends Actor {
override def receive = {
case subscribe: Subscribe =>
mediator forward subscribe
case unsubscribe: Unsubscribe =>
mediator forward unsubscribe
case publish: Publish =>
mediator.!(publish)
}
}
val proxy = system.actorOf(Props(new Proxy), "proxy")
proxy.!(Subscribe(topic,testActor))(testActor)
expectMsgType[SubscribeAck](2 seconds)
proxy ! Publish(topic, message)
expectMsg(5 seconds, message)
proxy.!(Unsubscribe(topic,testActor))(testActor)
expectMsgType[UnsubscribeAck](5 seconds)
proxy ! Publish(topic, message)
expectNoMsg(5 seconds)
}
test("Send message to address") {
val testActorAddress = testActor.path.toString
// val system2 = ActorSystem("test", MediatorTest.config)
// Cluster(system2).join(joinAddress)
mediator.!(Subscribe(topic, testActor))(testActor)
expectMsgType[SubscribeAck](5 seconds)
println(testActorAddress) // akka://test/system/testActor1
mediator.!(Publish(topic, message))(testActor)
expectMsg(2 seconds, message)
mediator ! Send(testActorAddress, message, false)
expectMsg(5 seconds, message)
}
}
Two things:
whether or not you use forward does not matter much, since you do not have a useful sender in scope in your test procedure (you are not mixing in ImplicitSender); but this is not the problem
you are not forwarding the Publish message, which is why it does not publish the message
Is there any way to use elementFormDefault="unqualified" server schema type with Spyne server?
Now my all trials end up with method response result:
<senv:Envelope xmlns:tns="http://test.com/remoteService/"
xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:testResponse>
<tns:status>ok</tns:status>
</tns:testResponse>
</senv:Body>
And generated wsdl fragment with "qualified" elementFormDefault :
<xs:schema targetNamespace="http://test.com/remoteService/" elementFormDefault="qualified"></xs:schema>
How to configure method or parameters model to get result like this:
<senv:Envelope xmlns:tns="http://test.com/remoteService/"
xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:testResponse>
<status>ok<status>
</tns:testResponse>
</senv:Body>
My goal is to generate result where child element:
<tns:status>ok</tns:status>
will appear without namespace prefix - like this:
<status>ok<status>
If you wonder how to add listener to the event_manager for method_return_string or for another event, see bellow a full example:
from spyne import Application, rpc, ServiceBase, Iterable, Integer, Unicode
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
class HelloWorldService(ServiceBase):
#rpc(Unicode, Integer, _returns=Iterable(Unicode))
def say_hello(ctx, name, times):
for i in range(times):
yield u'Hello, %s' % name
def on_method_return_string(ctx):
ctx.out_string[0] = ctx.out_string[0].replace(b'Hello>', b'Good by')
HelloWorldService.event_manager.add_listener('method_return_string',
on_method_return_string)
application = Application([HelloWorldService], 'spyne.examples.hello.soap',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11())
wsgi_application = WsgiApplication(application)
if __name__ == '__main__':
import logging
from wsgiref.simple_server import make_server
server = make_server('127.0.0.1', 8000, wsgi_application)
server.serve_forever()
As of Spyne 2.12 this is still the only way to remove namespaces from response variables.
As of 2.10, Spyne does not support this.
The patch would be a bit hairy. Chime in at soap#python.org if you're willing to work on this.
A workaround would be to remove namespace prefixes manually from outgoing documents in a method_return_document hook. If you need to enforce the same for incoming documents as well, you either have to modify the Wsdl as well in a document_built event, or use soft validation (soft validation does not care about namespaces) or no validation at all.