How to perform JDBC authentication with Scala's Slick library? - scala

Intro
I have a microservice written in Akka HTTP which communicates with the frontend via an REST API.
I use Scala's Slick library to communicate with the PostgreSQL DBMS.
I want secure the API with HTTP's basic authentication mechanism (the frontend sends an username and password to the microservice in each request).
The extract credentials from each HTTP API call I want to add into Slick's actions in order to ensure via "JDBC Authentication" that the user is authorized to manipulate a given set of data.
Problem
While its obvious for me how to accomplish this with plain Java's DriverManager by creating each new connection with a set of credentials and using this connection in order to perform future operations, the Slick library offers something like this:
val db = Database.forURL(jdbcUrl, superuser, password)
but the documentation states that only one such instance should be created for the whole life of an application:
Note: A Database object usually manages a thread pool and a connection pool. You should always shut it down properly when it is no longer needed (unless the JVM process terminates anyway). Do not create a new Database for every database operation. A single instance is meant to be kept alive for the entire lifetime your your application.
So I am stick to the one db singleton which always runs all actions with the superuser credentials.
I am new to Slick and I don't know how to add an additional username and password in order to send it together with an action (SQL, DDL statement) to the DBMS that can verify if the user is permit to perform this action. Here is an example of what I want to achieve:
override def removeDatabase(name: String): Future[Unit] =
db.run(sqlu"""DROP DATABASE IF EXISTS #${name.toLowerCase};""").map(_ => ())
To this drop action I would like add the credentials extract from an HTTP request in order to get an JDBC authentication to ensure that the current user performing this action is authorized to delete this database.
What I have did so far?
According to the Slick documentation:
In order to drop down to the JDBC level for functionality that is not available in Slick, you can use a SimpleDBIO action which is run on a database thread and gets access to the JDBC Connection:
val getAutoCommit = SimpleDBIO[Boolean](_.connection.getAutoCommit)
I tried this:
override def removeDatabase(name: String): Future[Unit] = {
val dropStatement = SimpleDBIO[Unit] { session => {
val currentConnection = session.connection
currentConnection.setClientInfo("username", "testUsername")
currentConnection.setClientInfo("password", "invalidPwd")
println(">>> username: " + currentConnection.getClientInfo("username"))
println(">>> password: " + currentConnection.getClientInfo("password"))
sqlu"""DROP DATABASE IF EXISTS #${name.toLowerCase};"""
}
}
db.run(dropStatement).map(_ => ())
}
But unfortunately it doesn't have any impact, the passed credentials to the client info are ignored:
>>> username: null
>>> password: null

You can create a custom Database like this:
import slick.jdbc.PostgresProfile.api._
val db: Database = Database.forConfig("myDatabase")
This uses the appropriate config from application.conf:
myDatabase = {
dataSourceClass = "org.postgresql.ds.PGSimpleDataSource"
properties = {
databaseName = "mydata"
user = "postgres"
password = "postgres"
}
numThreads = 4
}

Related

Slick Database Instantiation And Connection Pool Logic

I am instantiating a slick database with code similar to
import slick.jdbc.JdbcBackend.Database
val db : Database = Database forConfig "configPath"
The query is constructed from a function that takes in a user id and returns a user name from the database table:
def queryName(userId : String) =
for {
row <- TableQuery[Tables.MyTable] if row.userid === userId
} yield row.username
And then running the query to produce distinct Publisher values:
val p1 : Publisher[String] = db stream (queryName("foo").result)
val p2 : Publisher[String] = db stream (queryName("bar").result)
Finally, my question is: Do multiple calls to db.stream utilize the same connection in the connection pool?
In other words, once I've instantiated the database is that the same as locking in on a single connection?
The implication would be that true utilization of all connections in the pool would require a function to create Database values before querying:
//Is this necessary?
val db = () => Database forConfig "configPath"
val p1 = db() stream (queryName("foo").result)
Thank you in advance for your consideration and response
According to Slick documentation about database thread pool:
When using Database.forConfig, the thread pool is configured directly in the external configuration file together with the connection parameters.
My hypothesis here is that you are using a connection pool (which is always recommended in production environments) and you have configured it properly in the external configuration file (the one referred by configPath).
You don't have to worry about database connections since your Database object (your db) is managing that for you.
Each call to db.stream() actually uses (and withdraw) a connection from the pool (eventually opening a new one according to the pool size and configuration) and releases it afterwards back into the pool.
For further details on how a connection pool works and how to configure (e.g. size) it in slick can be found at connection-pools.
An addictional note from adding-slick-to-your-project:
If you want to use Slick’s connection pool support, you need to add HikariCP as a dependency.

Play (Scala), Reactivemongo and multiple databases

So, been working on a smaller project with Play, ReactiveMongo and MongoDB. My question is about the application.conf part for ReactiveMongo, have the standard googlable one:
mongodb = {
db = "db1"
servers = [ "localhost:27017" ]
credentials = {
username = "auser"
password = "apassword"
}
}
And to access a collection, in Scala:
def sessionCollection: JSONCollection = db.collection[JSONCollection]("session")
So, since MongoDB locks at database level for writes, I'm looking for a solution to use several databases.
Question is: how do I configure multiple databases so I can define collections like above, from those databases?
MongoDB 2.6.x, Play 2.3.x, Reactivemongo 0.10.5.0.akka23
Edit: I should say that I already know about this, doing it manually with code, but I wanted to know if there were any Play specific known solution I've failed to reach via Google.
In your Play application, you can use ReactiveMongo with multiple connection pools (possibly with different replica sets and/or different options), using the #NamedDatabase annotation.
Consider the following configuration, with several connection URIs.
# The default URI
mongodb.uri = "mongodb://someuser:somepasswd#localhost:27017/foo"
# Another one, named with 'bar'
mongodb.bar.uri = "mongodb://someuser:somepasswd#localhost:27017/lorem"
Then the dependency injection can select the API instances using the names.
import javax.inject.Inject
import play.modules.reactivemongo._
class MyComponent #Inject() (
val defaultApi: ReactiveMongoApi, // corresponds to 'mongodb.uri'
#NamedDatabase("bar") val barApi: ReactiveMongoApi // 'mongodb.bar'
) {
}

Set PostgreSQL configuration parameter in SQLAlchemy

I'm using Pyramid and SQLAlchemy for my REST server. For the logging purposes I need some way how to determine user name in postgresql after update trigger function. One way is setting some runtime configuration parameter before update execution and using this value in trigger function.
In psql terminal it can be done using
set myapp.user_name = 'User Name';
This parameter will be subsequently used in postgresql trigger.
Is it possible to set this parameter in Pyramid / SQLAlchemy application?
I suppose I can use SQLAlchemy Events. But I'm not sure which event is correct for this case.
You can register a Pyramid event handler which would set the parameter on any new request:
from pyramid import events
def on_new_request(event):
"""
you can access request as event.request
and the current registry settings as event.request.registry.settings
"""
session = DBSession()
session.execute("SET blah TO 'foo'")
def app(global_config, **settings):
config = Configurator(...)
config.add_subscriber(on_new_request, events.NewRequest)
See Deployment Settings and events.NewRequest for more details.
One thing to watch out is to make sure you're always using the same session object - SQLAlchemy maintains a pool of connections and, if you commit your session, all subsequent operations will use a brand-new session which is not pre-configured with your settings. In other words, you should not do session.commit(), session.rollback(), transaction.commit() etc. in your code and instead rely on ZopeTransactionExtension committing/rolling back the transaction at the end of the request/response cycle. Which is a recommended practice anyway.
I'm not sure what the exact command you're running with set myapp.user = "User Name".
When you say "In psql this can be done using..." I'm assuming you are running some sort of SQL command, although the command you listed is not a valid command, at least not typically.
In any event, arbitrary SQL can be executed in SQLAlchemy using the .execute() method.
session = Session()
session.execute("select 1;")
http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html?highlight=execute#sqlalchemy.orm.session.Session.execute

Connect with custom user name in Scala/Play using default tools

In my Scala/Play application i have to work with database but there's no default user and password because every authenticated application user is bound to different DB user. So i'd like to specify user name and password on obtaining connection, smth like:
DB.withConnection(user = "James", password = "secret") { ... }
For now i can't find such capabilities in docs (and honestly saying i'm not sure how to specify a search query for my question).
And another question: is it safe to store user password in session taking into account that session is stored on user side? Or are there any best practices for such case when different DB users work with app?
Answer to Question 1
In Play, you obtain datasources by name:
def getDataSource(name: String): DataSource
You'd have to do some heavy hacking of the stuff in package play.api.db to get the functionality you require.
That, or you can predefine a bunch of datasources if the number of users of your app is small, and retrieve the connection by their login name, e.g.:
db.bob.url="jdbc:h2:mem:db_for_bob"
db.bob.driver=org.h2.Driver
db.alice.url="jdbc:h2:mem:db_for_alice"
db.alice.driver=org.h2.Driver
And
DB.withConnection("bob") { implicit connection =>
Or
DB.withConnection(userNameKnownAtRuntime) { implicit connection =>
Answer to Question 2
Even though the data in sessions are heavily encrypted using the application secret, I would recommend not to store these client-side. Instead, implement something along the lines of Resource Owner Password Credentials Grant according to this section in the OAuth 2 spec. That would give your client a token which is only valid for a set period, and could be invalidated server-side if need be.

Re-using sessions in ScalaQuery?

I need to do small (but frequent) operations on my database, from one of my api methods. When I try wrapping them into "withSession" each time, I get terrible performance.
db withSession {
SomeTable.insert(a,b)
}
Running the above example 100 times takes 22 seconds. Running them all in a single session is instantaneous.
Is there a way to re-use the session in subsequent function invocations?
Do you have some type of connection pooling (see JDBC Connection Pooling: Connection Reuse?)? If not you'll be using a new connection for every withSession(...) and that is a very slow approach. See http://groups.google.com/group/scalaquery/browse_thread/thread/9c32a2211aa8cea9 for a description of how to use C3PO with ScalaQuery.
If you use a managed resource from an application server you'll usually get this for "free", but in stand-alone servers (for example jetty) you'll have to configure this yourself.
I'm probably stating the way too obvious, but you could just put more calls inside the withSession block like:
db withSession {
SomeTable.insert(a,b)
SomeOtherTable.insert(a,b)
}
Alternately you can create an implicit session, do your business, then close it when you're done:
implicit val session = db.createSession
SomeTable.insert(a,b)
SomeOtherTable.insert(a,b)
session.close