I am using two datasources in my Grails application. One is my local db and the other is remote db as shown below.
development {
dataSource {
url = "jdbc:mysql://localhost:3306/testax_dev?autoreconnect=true"
properties {
...
}
}
dataSource_phpscheduler {
driverClassName = 'com.mysql.jdbc.Driver'
username = "xyz"
password = "zxyz"
url = "jdbc:mysql://remote-ip:3306/phpscheduler?autoreconnect=true"
}
}
Now I want to handle the exception caused due to the connection problem of remote database so that the application starts successfully.
A Grails application will not start if the dataSource bean(s) can't be created. There is no way to start the application if the connection to the database fails.
The reason for this is that Grails depends on those beans being instanced, and ready.
Update
As pointed out by Burt Beckwith it is possible to do this, but it does require you to understand the lifecycle of Hibernate and your datasource. It's also possible to register your own dataSource which has error handling. As always, we owe much to Burt.
Related
Background:
In my project, I needed to have two factor authentication (by sending OTP to registered email) which I implemented by extending DaoAuthenticationProvider.
In order to identify reason of why OTP authentication has failed, my custom authentication provider throws new custom exceptions for example BadOtpException, ExpiredOtpException, ConsumedOtpException. All these exceptions are subclasses of BadCredentialsException. This authentication flow is working fine.
Issue:
The Authentication events that were earlier getting published while I was using DaoAuthenticationProvider are now not getting published with my custom authentication provider.
Cause:
Upon some troubleshooting I figured out that Grails Spring Security Core plugin uses DefaultAuthenticationEventPublisher to publish the events. And this class publishes events on the basis of exception mappings which contain exception name vs event name to resolve the event that needs to be published whenever exception occurs. And these mappings are configured in its constructor.
Since mappings of my custom exceptions are not present in this constructor, the authentication failure events are not getting published.
Tried Solution:
I tried overriding DefaultAuthenticationEventPublisher and added new exception mappings by invoking super.setAdditionalExceptionMappings(). Here is the custom event publisher:
class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
CustomAuthenticationEventPublisher() {
}
CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
super(applicationEventPublisher)
println('CustomAuthenticationEventPublisher')
Properties exceptionMappings = new Properties()
exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
super.setAdditionalExceptionMappings(exceptionMappings)
}
}
In resources.groovy, I registered my custom event publisher using following:
beans = {
authenticationEventPublisher(CustomAuthenticationEventPublisher)
}
But above solution is not working. Even the println() statement in the constructor is not getting logged. Seems like the bean is not getting registered.
Am I doing anything wrong in the above solution?
Is there any other way I can override the exception mappings?
With bit of more troubleshooting, I realized that zero argument constructor of the class CustomAuthenticationEventPublisher was being called instead of the other one.
So I tried setting the exception mappings in constructor and it worked.
Here is the code that worked form me:
class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
CustomAuthenticationEventPublisher() {
println('CustomAuthenticationEventPublisher')
Properties exceptionMappings = new Properties()
exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
super.setAdditionalExceptionMappings(exceptionMappings)
}
CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
super(applicationEventPublisher)
}
}
Thanks.
It seems that Grails is trying to access my database when I first deploy it to my production tomcat server. I know this because I get the following error message in the stacktrace.log
invalid username/password; login denied
Now, I disabled database creation
dataSource {
pooled = false
driverClassName = "oracle.jdbc.OracleDriver"
dialect = org.hibernate.dialect.Oracle10gDialect
}
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = false
cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
//Set jdbc metadata to false to not open a session
temp.use_jdbc_metadata_defaults = false
}
production {
dataSource {
dbCreate = "none"
url = "jdbc:oracle:thin:#1.1.1.1:1521:xe"
}
}
I don't supply the database password because we use database users for authentication and authorization (please don't criticize this decision, I know it's awful, but we have a legacy database). So the username/password is supplied when the user makes a request through the client. We used http://sergiosmind.wordpress.com/2013/03/14/grails-using-a-database-user-for-security-login/ to set this up.
It seems that the Grails app cannot start because of this. Why is Grails accessing the database? What is it trying to do?
Grails uses connections at startup to initialize GORM - there's one to detect the dialect, one to configure the LOB handler, and Hibernate connects to initialize its configuration also.
I discuss this in these two blog posts: http://burtbeckwith.com/blog/?p=312 and http://burtbeckwith.com/blog/?p=1565
How do you use a JDBCRealm to handle authenticating and authorizing users in servlets? The only example I can find is to create the DataSource in web.xml (such as Authentication against database using shiro 1.2.1).
I do not want to include database credentials in my source tree (for obvious reasons) and would prefer to use a Context defined DataSource via JNDI as I have for every other RDBMS I have used for any other purpose in every other servlet project I have developed.
How do you configure a Shiro JDBCRealm to obtain its DataSource from JNDI?
Vrushank's answer was really close: you don't need to subclass the JdbcRealm here - you can use Shiro's JndiObjectFactory to acquire the DataSource and then reference that DataSource when you configure the JdbcRealm:
[main]
dataSource = org.apache.shiro.jndi.JndiObjectFactory
dataSource.resourceName = java://app/jdbc/myDataSource
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource
#addt'l config
For a web application, save the file under WEB-INF/shiro.ini.
See Also
https://github.com/danielmt/shiro-primefaces-example/blob/master/src/main/webapp/WEB-INF/shiro.ini
For Shiro to work with permissions with the JDBC realm this parameter is indispensable:
jdbcRealm.permissionsLookupEnabled = true
I wasted many hours on this because the default for this option is false. In other words, if you don't put this option Shiro always return an empty list of permissions.
I commented on #Les Hazlewood answer and on #Recurse comment, but might be that new answer is better option.
In my case I have to use only JDNI datasource name on weblogic and full path on tomcat:
Tomcat:
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = java:/comp/env/oracle/pportal_dev
# JDBC realm config
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $ds
Weblogic
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = oracle/pportal_dev
# JDBC realm config
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource = $ds
Note
ds.resourceName = java:/comp/env/oracle/pportal_dev
vs
ds.resourceName = oracle/pportal_dev
You'll need to create a custom Realm of your own by extending JdbcRealm to programatically lookup the datasource through the provided JNDI.
You can then pass the JNDI as a property in shiro.ini
[main]
# realms to be used
customSecurityRealm=package.to.your.CustomRealm
customSecurityRealm.jndiDataSourceName=java:app/jdbc/myDatasource
See the below article as an example. It takes care of both Authentication and Authorization.
Apache Shiro JDBC Realm
We are using grails with groovy and recently changed database from MySQL to Oracle 11g. We took care of table names like USER, RESOURCE to make it something else, remapped the new names in the domain classes.
I also added some default data in roles from mysql table(for spring security to work) and inserted one user 'admin' manually in GRAUSER table (renamed from USER).
The server does start up in Netbeans
But when I try to login I get the following error
ERROR util.JDBCExceptionReporter ORA-00904: "THIS_"."password": invalid identifier
Not able to debug the cause of this. Let me know if any more details/code is needed to review, but I need to be able to login to the application.
Could you post your DataSource.groovy file? Below is roughly what mine looks like for connecting to Oracle.
dataSource {
logsql = true
pooled = true
driverClassName = "oracle.jdbc.OracleDriver"
username = "user"
password = "secret"
dialect='org.hibernate.dialect.Oracle10gDialect'
}
environments {
development {
dataSource {
//dbCreate = "create-drop" // one of 'create', 'create-drop','update'
url = "jdbc:oracle:thin:#server:1521:sid"
}
}
}
Everytime i call subject.isPermitted(), it sends a sql to db.
How can i cache it? Any example? Thanks.
I read the doc of shiro grails plugin, but cant solove it.
DataSource:
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = true
cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
How to set the cachemanager to shiro? I try spring.resource,throw an error.
What's the instance bean name of cachemanager? Do i need to config sth else?
You'll need to configure an org.apache.shiro.cache.CacheManager instance on Shiro's SecurityManager. Most of Shiro's out-of-the-box Realm implementations know how to work with a configured CacheManager and will cache AuthorizationInfo returned from a Realm permission lookup automatically.
I'm not sure how to do this using the Grails Shiro plugin, but in Shiro's INI, you would do that this way:
[main]
...
cacheManager = com.my.implementation.of.CacheManager
securityManager.cacheManager = $cacheManager
...
I'd recommend asking the grails-user mailing list to see if there is a more 'grailsy' way to configure this for the Grails Shiro plugin.
HTH,
Les