Configuring grails datasources with username and password from OS environment - grails

I am currently working on a grails application. Previously I have been using Django alot to create webapps. In django you can easily create settings that are collected from the OS environment. I find that to be an easy solution for making sure that you don't check in usernames and passwords into the source code repository.
So what I would like to do is in the DataSource.groovy be able to lift in the username and password from the OS environment. Has anyone done anything like this, or is this not the way to go forward?
If this is not the "grails way", how is it supposed to be done, because having the username and password in the repository just feels wrong?

You can write code in DataSource.groovy and Config.groovy to get env variable and then set username password and other things. I always to it for my production app, sample code is as follows
//Eample is based on url structure like "mysql://username:password#host/database?autoReconnect=true&useUnicode=yes&characterEncoding=UTF-8"
dataSource {
String mysqlUrl = System.getenv("DATABASE_URL")
println ">>>>>> Got DATABASE_URL: ${mysqlUrl} <<<<<<<"
URI dbUri = new URI(mysqlUrl);
username = dbUri.userInfo.split(":")[0]
password = dbUri.userInfo.split(":")[1]
String databaseUrl = "jdbc:${dbUri.scheme}://${dbUri.host}${dbUri.path}"
if (dbUri.port > 0) {
databaseUrl += ":${dbUri.port}"
}
String query = dbUri.query ?: "reconnect=true"
query += "&autoReconnect=true&useUnicode=yes&characterEncoding=UTF-8"
databaseUrl += "?${query}"
url = databaseUrl
dialect = 'org.hibernate.dialect.MySQL5InnoDBDialect'
}
This is one of the way I am sure there must be some simpler way to do this.
PS: I find it simple though :)

You can create a environment variable in Windows. Example DBCONNGRAILS. In config.groovy (...\grails-app\conf\config.groovy) you can use something like
def myConnectionString = System.getenv(DBCONNGRAILS)
WARNING: If you get variables from the environment(Windows) there will no problem when loading the value when in development. But when you are working with a production server, there will be cases when you will need to resart the OS for tomcat to be able to pick up the changes. I would recommend externalizing the config files.

You can externalized the configuration in any other place and load/merge it simply in your application configuration.

We use JNDI at work for exactly this scenario. We have context.xml files located within the individual server's Tomcat directories. The context.xml contains the datasource properties and then within the datasource closure (defined in Datasource.groovy), the jndiName property is set accordingly.
More Links about JNDI:
http://grails.github.io/grails-doc/2.3.7/guide/conf.html#JNDIDataSources
http://grails.asia/how-to-make-grails-use-jndi-datasource-with-tomcat

Related

Is it possible to reference variables defined in different configuration files in grails?

I've seen several similar questions but I haven't found a solution. Apologies if this is a duplicate.
My datasource is MongoDB and its properties are being defined in an external configuration file by the user like so :
External Datasource File
mongo {
host = "mongohost"
port = 27017
databaseName = "myDatabase"
}
This is fine and its been working well for quite a while.
The problem has arisen now that I've added quartz to the application. I need to define the datasource for quartz in a Quartz configuration file.
QuartzConfig.groovy
quartz {
autoStartup = true
jdbcStore = false
waitForJobsToCompleteOnShutdown = true
exposeSchedulerInRepository = false
props {
scheduler.skipUpdateCheck = true
jobStore.class="com.novemberain.quartz.mongodb.MongoDBJobStore"
jobStore.mongoUri="${mongo.host}"
jobStore.addresses="${mongo.host}"
jobStore.dbName="quartz"
jobStore.collectionPrefix="quartz"
threadPool.threadCount=1
}
}
The host should be the same as the user defined host in the external datasource. I can't seem to be able to access the mongo.host variable no matter what I try. One option would be to require the user to specify quartz configuration in the external file too but I don't want to do that as I want to keep that file as basic as possible. I'd like to just reuse the mongo.host variable.
Does anyone know how?
Note
I'm using the quartz-mnongo class developed here https://github.com/michaelklishin/quartz-mongodb if anyone who is using quartz stumbles across the question and finds it strange that I have mongo configuration options.

How do I derive physical path of a relative directory inside Config.groovy?

I am trying to set up Weceem using the source from GitHub. It requires a physical path definition for the uploads directory, and for a directory for appears to be used for writing searchable indexes. The default setting for uploads is:
weceem.upload.dir = 'file:/var/www/weceem.org/uploads/'
I would like to define those using relative paths like WEB-INF/resources/uploads. I tried a methodology I have used previously for accessing directories with relative path like this:
File uploadDirectory = ApplicationHolder.application.parentContext.getResource("WEB-INF/resources/uploads").file
def absoluteUploadDirectory = uploadDirectory.absolutePath
weceem.upload.dir = 'file:'+absoluteUploadDirectory
However, 'parentContext' under ApplicationHolder.application is NULL. Can anyone offer a solution to this that would allow me to use relative paths?
look at your Config.groovy you should have (maybe it is commented)
// locations to search for config files that get merged into the main config
// config files can either be Java properties files or ConfigSlurper scripts
// "classpath:${appName}-config.properties", "classpath:${appName}-config.groovy",
grails.config.locations = [
"file:${userHome}/.grails/${appName}-config.properties",
"file:${userHome}/.grails/${appName}-config.groovy"
]
Create Conig file in deployment server
"${userHome}/.grails/${appName}-config.properties"
And define your prop (even not relative path) in that config file.
To add to Aram Arabyan's response, which is correct, but lacks an explanation:
Grails apps don't have a "local" directory, like a PHP app would have. They should be (for production) deployed in a servlet container. The location of that content is should not be considered writable, as it can get wiped out on the next deployment.
In short: think of your deployed application as a compiled binary.
Instead, choose a specific location somewhere on your server for the uploads to live, preferably outside the web server's path, so they can't be accessed directly. That's why Weceem defaults to a custom folder under /var/www/weceem.org/.
If you configure a path using the externalized configuration technique, you can then have a path specific to the server, and include a different path on your development machine.
In both cases, however, you should use absolute paths, or at least paths relative to known directories.
i.e.
String base = System.properties['base.dir']
println "config: ${base}/web-app/config/HookConfig.grooy"
String str = new File("${base}/web-app/config/HookConfig.groovy").text
return new ConfigSlurper().parse(str)
or
def grailsApplication
private getConfig() {
String str = grailsApplication.parentContext.getResource("config/HookConfig.groovy").file.text
return new ConfigSlurper().parse(str)
}

What is the best way to modify a project configuration from within a plugin?

As I am trying to write a Grails Plugin, I stumbled upon two problems:
how do I modify one of the configuration files like Config.groovy or DataSource.groovy from witin the _install.groovy script? It is easy to append something to those files, but how do I modify it in a clean way? text.replaceAll()? Or should I create a new config file?
how do I get the name of the current application into which the plugin will be installed? I tried to use app.name and appName but both do not work.
Is there maybe somewhere a good tutorial on creating plugins which I haven't found yet?
Here is an example of editing configuration files from scripts/_Install.groovy.
My plugin copies three files to the target directory.
.hgignore is used for version control,
DataSource.groovy replaces the default version, and
SecurityConfig.groovy contains extra settings.
I prefer to edit the application's files as little as possible, especially because I expect to change the security setup a few years down the road. I also need to use properties from a jcc-server-config.properties file which is customized for each application server in our system.
Copying the files is easy.
println ('* copying .hgignore ')
ant.copy(file: "${pluginBasedir}/src/samples/.hgignore",
todir: "${basedir}")
println ('* copying SecurityConfig.groovy')
ant.copy(file: "${pluginBasedir}/src/samples/SecurityConfig.groovy",
todir: "${basedir}/grails-app/conf")
println ('* copying DataSource.groovy')
ant.copy(file: "${pluginBasedir}/src/samples/DataSource.groovy",
todir: "${basedir}/grails-app/conf")
The hard part is getting Grails to pick up the new configuration file. To do this, I have to edit the application's grails-app/conf/Config.groovy. I will add two configuration files to be found on the classpath.
println ('* Adding configuration files to grails.config.locations');
// Add configuration files to grails.config.locations.
def newConfigFiles = ["classpath:jcc-server-config.properties",
"classpath:SecurityConfig.groovy"]
// Get the application's Config.groovy file
def cfg = new File("${basedir}/grails-app/conf/Config.groovy");
def cfgText = cfg.text
def appendedText = new StringWriter()
appendedText.println ""
appendedText.println ("// Added by edu-sunyjcc-addons plugin");
// Slurp the configuration so we can look at grails.config.locations.
def config = new ConfigSlurper().parse(cfg.toURL());
// If it isn't defined, create it as a list.
if (config.grails.config.locations.getClass() == groovy.util.ConfigObject) {
appendedText.println('grails.config.locations = []');
} else {
// Don't add configuration files that are already on the list.
newConfigFiles = newConfigFiles.grep {
!config.grails.config.locations.contains(it)
};
}
// Add each surviving location to the list.
newConfigFiles.each {
// The name will have quotes around it...
appendedText.println "grails.config.locations << \"$it\"";
}
// Write the new configuration code to the end of Config.groovy.
cfg.append(appendedText.toString());
The only problem is adding SecurityConfig.groovy to the classpath. I found that you can do that by creating the following event in the plugin's /scripts/Events.groovy.
eventCompileEnd = {
ant.copy(todir:classesDirPath) {
fileset(file:"${basedir}/grails-app/conf/SecurityConfig.groovy")
}
}
Ed.
You might try changing the configuration within the MyNiftyPlugin.groovy file (assuming that your plugin is named my-nifty). I've found that I can change the configuration values within the doWithApplicationContext closure. Here's an example.
def doWithApplicationContext = { applicationContext ->
def config = application.config;
config.edu.mycollege.server.name = 'http://localhost:8080'
config.edu.mycollege.server.instance = 'pprd'
}
The values you enter here do show up in the grailsApplication.config variable at run time. If it works for you, it will be a neater solution, because it doesn't require changes to the client project.
I must qualify that with the fact that I wasn't able to get Spring Security to work by this technique. I believe that my plugin (which depends on Spring Security) was loaded after the security was initialized. I decided to add an extra file to the grails-app/conf directory.
HTH.
For modifying configuration files, you should use ConfigSlurper:
def configParser = new ConfigSlurper(grailsSettings.grailsEnv)
configParser.binding = [userHome: userHome]
def config = configParser.parse(new URL("file:./grails-app/conf/Config.groovy"))
If you need to get application name from script, try:
metadata.'app.name'

hiding database password using codec specification in datasource.groovy not working

I am hiding the database password in datasource.groovy by doing
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
username = "root"
password = "q59YgJCdHw3dshwlsa=="
passwordEncryptionCodec = DESCodec
dbname="mydbname"
}
followed the artilce : http://jira.grails.org/browse/GRAILS-3620
I run the groovy codec class separately to get the encrypted string like this:
groovy grails-app/utils/DESCodec.groovy mypassword_string_text.
But After placing the DESCodec class in the Utility package in grails, when I try to start the server, it wont start but directly shutdown, It starts when I directly put the right password and comment out the codec and encrypted string. I am assuming it is not finding the codec class/anything else I am missing in the config like specifying the codec class requires quotes or any path change or I should change the algorithm, by the way the DESCodec class is the last codec class in the link.
I am working on getting the config to be environment specific and externalized, but I still need the password to be encrypted here, and then decrypted before connecting to the database.
I think the best way to do this is to externalise environment specific configuration (especially passwords, but URL's, email addresses and so on too) and then protect the config file with proper permissions on the file system of the target machine.
In Config.groovy (for example):
grails.config.locations = [
"file:/etc/${appName}/conf/db.properties"
]
and in the config file:
dataSource.username = "root"
dataSource.password = "secret"
I typically do this for production config, but keep dev/test config in the Config.groovy for convenience. When running in dev/test you simply get a warning at startup if it can't find the referenced config file. If it does find it, then it will override what's in Config.groovy
This has the added advantage of not requiring your war file to be recompiled and redeployed if any prod environment configuration changes, you simply change the config file and restart the app.

Grails External Config Read Incorrectly on First Load

Grails 1.3.7
I have some configuration located in an external config file. One of the entires looks like this:
site.maintenance.mode = false
I have a filter which checks for certain config settings for specific URLs. When I do a run-app or deploy a WAR into Tomcat and do:
boolean maintenanceMode = grailsApplication.config.site.maintenance.mode
maintenanceMode is coming back true. If I look at the config object in debug mode, this is what I get:
site={maintenance={mode=false, message="<p>Our trail guides are working hard to get the system back on track.</p><p>We're sorry, the account system is down for maintenance at the moment. We'll get it back online as quickly as we can. Thanks for your patience.</p>"}}
I have a controller that I use to reload this config file dynamically and hitting this controller will fix the issue. But I'm curious as to why it is incorrect on first runs and why the discrepency in what is getting put in the maintenanceMode variable vs what is actually in the config object.
Are you using a Java properties file or a Groovy file? If you're using a properties file then I believe Grails will interpret site.maintenance.mode=false the same way as site.maintenance.mode='false' and since Groovy will interpret:
"false".asBoolean() == true
then that would explain why you would see that initial true value.
I just ran a simple test locally to verify this behavior. When I externalize my properties in a file called test.properties then site.maintenance.mode=false initially gets a boolean value of true, when I use a file called test.groovy then it interprets the boolean value of site.maintenance.mode=false as false. I believe this is because when you use a Groovy file Grails uses ConfigurationSlurper to process it but when you use a properties file Grails interprets everything as String name/value pairs.
What I do is to have an external Config.groovy file, for instance: MyConfig.groovy
At the end of the standard grails Config.groovy file, I have the following:
def ENV_NAME = "MY_EXTERNAL_CONFIG"
if(!grails.config.locations || !(grails.config.locations instanceof List)) {
grails.config.locations = []
}
if(System.getenv(ENV_NAME)) {
grails.config.locations << "file:" + System.getenv(ENV_NAME)
} else if(System.getProperty(ENV_NAME)) {
grails.config.locations << "file:" + System.getProperty(ENV_NAME)
} else {
println "No external Configs found."
}
So now you can have a MyConfig.groovy file anywhere in production environment (for example) and then set an Environment system variable to point to this file (or pass it as parameter to startup.sh), before you start tomcat:
MY_EXTERNAL_CONFIG="/home/tomcat/configs/MyConfig.groovy"
export MY_EXTERNAL_CONFIG
That's it. Now you have an external MyConfig.groovy file. The properties in it are accessible from your grails app as they were part of the standard Config.groovy
import org.codehaus.groovy.grails.commons.*
//...
ConfigurationHolder.config.foo.bar.hello

Resources