How to customize an existing Grails plugin functionality, modifying behavior of doWithSpring method - grails

I am new to grails and while working with Spring Security LDAP plugin it was identified that it accepts the ldap server password in plain text only. The task in hand is to pass an encrypted password which is decrypted before it is consumed by the plugin during its initialization phase.
I have already searched for all possible blogs and stackoverflow questions but could not find a way to extend the main plugin class to simply override the doWithSpring() method so that i can simply add the required decryption logic for the Ldap server password. Any help here will be appreciated.
I have already seen and tried jasypt plugin but it also does not work well if the password is stored in some external file and not application yml. So I am looking for a solution to extend the Spring security plugin main class, add the required behavior and register the custom class.
EDIT
Adding the snippet from Grails LDAP Security plugin, which I am trying to override. So If i am successfully able to update the value of securityConfig object before the plugin loads, the purpose is solved.
Some snippet from the plugin:
def conf = SpringSecurityUtils.securityConfig
...
...
contextSource(DefaultSpringSecurityContextSource, conf.ldap.context.server) { // 'ldap://localhost:389'
authenticationSource = ref('ldapAuthenticationSource')
authenticationStrategy = ref('authenticationStrategy')
userDn = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**password = conf.ldap.context.managerPassword // 'secret'**
contextFactory = contextFactoryClass
dirObjectFactory = dirObjectFactoryClass
baseEnvironmentProperties = conf.ldap.context.baseEnvironmentProperties // none
cacheEnvironmentProperties = conf.ldap.context.cacheEnvironmentProperties // true
anonymousReadOnly = conf.ldap.context.anonymousReadOnly // false
referral = conf.ldap.context.referral // null
}
ldapAuthenticationSource(SimpleAuthenticationSource) {
principal = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**credentials = conf.ldap.context.managerPassword // 'secret'**
}

You don't need to override the doWithSpring() method in the existing plugin. You can provide your own plugin which loads after the one you want to affect and have your doWithSpring() add whatever you want to the context. If you add beans with the same name as the ones added by the other plugin, yours will replace the ones provided by the other plugin as long as you configure your plugin to load after the other one. Similarly, you could do the same think in resources.groovy of the app if you don't want to write a plugin for this.
You have other options too. You could write a bean post processor or bean definition post processor that affects the beans created by the other plugin. Depending on the particulars, that might be a better idea.
EDIT:
After seeing your comment below I created a simple example that shows how you might use a definition post processor. See the project at https://github.com/jeffbrown/postprocessordemo.
The interesting bits:
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomeBean.groovy
package demo
class SomeBean {
String someValue
}
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomePostProcessor.groovy
package demo
import org.springframework.beans.BeansException
import org.springframework.beans.MutablePropertyValues
import org.springframework.beans.PropertyValue
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
class SomePostProcessor implements BeanDefinitionRegistryPostProcessor{
#Override
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition definition = registry.getBeanDefinition('someBean')
MutablePropertyValues values = definition.getPropertyValues()
PropertyValue value = values.getPropertyValue('someValue')
def originalValue = value.getValue()
// this is where you could do your decrypting...
values.addPropertyValue('someValue', "MODIFIED: ${originalValue}".toString())
}
#Override
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/conf/spring/resources.groovy
beans = {
someBean(demo.SomeBean) {
someValue = 'Some Value'
}
somePostProcessor demo.SomePostProcessor
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/init/postprocessordemo/BootStrap.groovy
package postprocessordemo
import demo.SomeBean
class BootStrap {
SomeBean someBean
def init = { servletContext ->
log.info "The Value: ${someBean.someValue}"
}
def destroy = {
}
}
At application startup you will see log output that looks something like this...
2017-10-23 19:04:54.356 INFO --- [ main] postprocessordemo.BootStrap : The Value: MODIFIED: Some Value
The "MODIFIED" there is evidence that the bean definition post processor modified the property value in the bean. In my example I am simply prepending some text to the string. In your implementation you could decrypt a password or do whatever you want to do there.
I hope that helps.

After trying Jasypt plugin and BeanPostProcessor solutions unsuccessfully for my use case, I found below solution to work perfectly.
To describe again the problem statement here,
a) we had to keep the passwords in an encrypted format inside properties files
b) and given we were packaging as a war file so the properties must not be kept inside the war to allow automated deployment scripts update the encrypted passwords depending on the environment
Jasypt plugin was a perfect solution for the use case a), but it was not able to cover the b) scenario
Moreover, the Grails LDAP Security plugin was getting loaded quite early hence Bean Post processors were also not helping out here.
Solution:
Created a new class by implementing the interface SpringApplicationRunListener. Extended its methods and parsed the properties file using YamlPropertySourceLoader
Sample code:
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load(
"application.yml", new ClassPathResource("application.yml"),"default");
return applicationYamlPropertySource;
Once the properties were loaded inside the MapPropertySource object, parsed them for the encrypted values and applied the decryption logic.
This whole implementation was executed before any plugins were initialized during Grails bootup process solving the purpose.
Hope it will help others.

Related

Grails 4 how to get an handle to artifacts in custom command

I need to build a custom command in a Grails 4 application (https://docs.grails.org/4.0.11/guide/single.html#creatingCustomCommands), and I need to get an handle to some Grails Services and Domain classes which I will query as needed.
The custom command skeleton is quite simple:
import grails.dev.commands.*
import org.apache.maven.artifact.Artifact
class HelloWorldCommand implements GrailsApplicationCommand {
boolean handle() {
return true
}
}
While the documentation says that a custom command has access to the whole application context, I haven't found any examples on how to get an handle of that and start accessing the various application artifacts.
Any hints?
EDIT: to add context and clarify the goal of the custom command in order for further recommendation/best practices/etc.: the command reads data from a file in a custom format, persist the data, and writes reports in another custom format.
Will eventually be replaced by a recurrent job, once the data will be available on demand from a third party REST API.
See the project at github.com/jeffbrown/marco-vittorini-orgeas-artifacts-cli.
grails-app/services/marco/vittorini/orgeas/artifacts/cli/GreetingService.groovy
package marco.vittorini.orgeas.artifacts.cli
class GreetingService {
String greeting = 'Hello World'
}
grails-app/commands/marco/vittorini/orgeas/artifacts/cli/HelloCommand.groovy
package marco.vittorini.orgeas.artifacts.cli
import grails.dev.commands.*
class HelloCommand implements GrailsApplicationCommand {
GreetingService greetingService
boolean handle() {
println greetingService.greeting
return true
}
}
EDIT:
I have added a commit at github.com/jeffbrown/marco-vittorini-orgeas-artifacts-cli/commit/49a846e3902073f8ea0539fcde550f6d002b9d89 which demonstrates accessing a domain class, which was part of the question I overlooked when writing the initial answer.

How to use MapStruct with Grails?

I have a need in my Grails project to map objects from one type (which is typically a Map) to another type (some POGO). There are some great examples for getting this working with Boot Spring, but not a lot for Grails.
I added the following lines to my build.gradle:
compile 'org.mapstruct:mapstruct:1.4.2.Final'
compileOnly 'org.mapstruct:mapstruct-processor:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
...
compileJava {
options.annotationProcessorPath = configurations.annotationProcessor
// if you need to configure mapstruct component model
options.compilerArgs << "-Amapstruct.defaultComponentModel=spring"
}
I have a mapper defined as:
#Mapper(componentModel = "spring")
public interface AuthResponseMapper2
{
#Mapping(source = "access_token", target = "token")
#Mapping(source = "token_type", target = "type")
GetTokenResponse toGetTokenResponse(Map map);
}
However, I don't see a generated class for that mapper; when I try to use it, I get a NoSuchBeanDefinitionException. Not sure where to go from here.
Best is to check if a AuthResponseMapper2Impl class is generated by MapStruct. If it is you need to make sure that the package is scanned by Spring Boot and picked up as a component.
In addition to that mapping from Map into a POJO is currently not supported you can follow mapstruct/mapstruct#1072 for this feature request

Configure Dropwizard ObjectMapper for configuration to ignore unknown

With an ObjectMapper (com.fasterxml.jackson.databind) it's possible to specify that it should ignore unknown properties. This can either be done by adding #JsonIgnoreProperties(ignoreUnknown = true) on a class level or by setting it as default behavior in the mapper. However, when doing this in the initialize() method of Application<MyConfiguration> it doesn't seem to have an effect.
ObjectMapper mapper = bootstrap.getObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
It still fails for unknown properties in the configuration file. How does one configure Dropwizard to ignore unknown properties?
The reason configuring DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES for bootstrap.getObjectMapper() doesn't have the desired effect is that ConfigurationFactory (the class later on used to parse the configuration) is enabling that particular feature of the object mapper in its constructor (see here):
public ConfigurationFactory(Class<T> klass,
Validator validator,
ObjectMapper objectMapper,
String propertyPrefix) {
...
this.mapper = objectMapper.copy();
mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
...
}
It's not possible to change the behaviour of ConfigurationFactory directly but Dropwizard provides means to override the factory that creates it, ConfigurationFactoryFactory, via Boostrap.setConfigurationFactoryFactory(). This allows to replace the real ObjectMapper with a proxy that doesn't allow to override the configuration and pass it to ConfigurationFactory:
bootstrap.setConfigurationFactoryFactory(
(klass, validator, objectMapper, propertyPrefix) -> {
return new ConfigurationFactory<>(klass, validator,
new ObjectMapperProxy(objectMapper), propertyPrefix);
}
);
The code for ObjectMapperProxy that ignores attempts to enable FAIL_ON_UNKNOWN_PROPERTIES it below:
private static class ObjectMapperProxy extends ObjectMapper {
private ObjectMapperProxy(ObjectMapper objectMapper) {
super(objectMapper);
}
private ObjectMapperProxy(ObjectMapperProxy proxy) {
super(proxy);
}
#Override
public ObjectMapper enable(DeserializationFeature feature) {
// do not allow Dropwizard to enable the feature
if (!feature.equals(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
super.enable(feature);
}
return this;
}
#Override
public ObjectMapper copy() {
return new ObjectMapperProxy(this);
}
}
Note that besides overriding enable to skip FAIL_ON_UNKNOWN_PROPERTIES copy is also implemented (together with an additional constructor) as ConfigurationFactory requires the object mapper to support copying.
While the solution above works it's obviously a workaround and I suggest upgrading to a newer Dropwizard version instead. The new Dropwizard makes ObjectMapper configuration easier to override (e.g. see this Dropwizard commit available in Dropwizard 1.1.x).
You need to disable that feature with:
bootstrap.getObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
UPDATE: The feature disable works for the API resources but not for the configuration YAML. Instead, you need to add the annotation below (same as mentioned on the question) to the configuration class:
#JsonIgnoreProperties(ignoreUnknown = true)

Replace Grails plugin listener

I need to extend the listener class of audit-logging plugin, in order to provide different functionality on some scenarios.
For that purpose I have created this class:
class CustomAuditLogListener extends AuditLogListener{
public CustomAuditLogListener(Datastore datastore) {
super(datastore)
}
#Override
protected void onPreUpdate(event) {
. . .
}
}
Now, in order for the plugin to use this class instead of the default
AuditLogListener, within Bootstrap.groovy I try to remove AuditLogListener from the application listeners, and add the custom listener:
def applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
log.debug("Application listeners - PRE")
log.debug(applicationContext.getApplicationListeners())
// Remove all the listeners registered by the audit plugin.
applicationContext.getApplicationListeners.each { listener ->
if (listener.class == AuditLogListener.class) {
servletContext.getApplicationListeners.remove(listener)
log.debug("AuditLogListener removed ")
}
}
grailsApplication.mainContext.eventTriggeringInterceptor.datastores.each { key, datastore ->
// Don't register the listener if we are disabled
if (!grailsApplication.config.auditLog.disabled && !datastore.config.auditLog.disabled) {
def listener = new CustomAuditLogListener(datastore)
listener.with {
// some options
}
applicationContext.addApplicationListener(listener)
log.debug("Add new listener CustomAuditLogListener")
}
}
log.debug("Application listeners -POST")
log.debug(applicationContext.getApplicationListeners())
Application listeners - PRE returns empty list
Add new Listener CustomAudtilogListener is logged
Application Listeners - POST returns empty list
The way I register my listener is copied from the way the plugin registers the listener, seen in this class
To sum, what I need is replacing AuditLogListener, with my own CustomAuditLogListener. Currently the above process is not working. Any suggestions?
Hey sorry but the post does seem a little confusing, although I think I understand
I need to extend the listener class of audit-logging plugin.
Now, in order for the plugin to use this class instead of the default
AuditLogListener,
so you are trying to override the auditLogListener.
Your problem maybe that you are not declaring it as an override in your conf/spring/resources.groovy.
I have done a similar thing for a class rather than listener here:
https://github.com/vahidhedayati/testwschat/blob/master/test/unit/anythingbut/grails/plugin/wschat/MyOverrideServiceSpec.groovy
https://github.com/vahidhedayati/testwschat/blob/master/grails-app/conf/spring/resources.groovy
and something similar here:
grails kickstart plugin KickstartFilters how to prevent password information on logs
basically once you declare your bean
yourAppFilters(KickstartFilters)
This then tricks your grails-app and any plugins that maybe referring to KickstartFilters to now refer to yourAppFilters as the replacement
Try a bean like this: (ctrl shift o) to pull in CustomAuditLogListener
AuditLogListener(CustomAuditLogListener){
grailsApplication = ref('grailsApplication')
}

access configuration/property files from src/groovy

I have a file under src/groovy and I have some properties that are in my Config.groovy and in external property file too. Normally if one want access properties its possible to use grailsApplication .configuration.property.name expression. I want to be able to access all those properties from this file that is under src/groovy directory. What I've tried so far
import grails.util.Holders
class ForkedTomcatCustomizer {
def application
void customize(Tomcat tomcat) {
println Holders.grailsApplication.config.property.name
}
}
gave me NPE saying that grailsAppliction is null
import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class ForkedTomcatCustomizer {
def application
void customize(Tomcat tomcat) {
def ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
def grailsAppliction = ctx.grailsApplication.getObject()
println grailsAppliction.config.property.name
}
}
the same - NPE because grailsAppliction is null
Is it possible to handle this situation somehow? Thank you!
Use the below and see if it works
println Holders.config.property.name
You don't need grailsApplication when using Holders.
The examples below are probably a little more complex than what you need, but they show how to get a configuration property at build time. I use them to merge two configuration files, but you might not need to do that.
This method returns a config property when called here at the CompileEnd event.
You could define a similar method in your app's _Events.groovy file that calls your own configuration holder class.
import org.codehaus.groovy.grails.commons.ConfigurationHolder;
class KeyAndSecret{
public static String consumerKey = ConfigurationHolder.config.consumerKey;
public static String consumerSecret = ConfigurationHolder.config.consumerSecret;
}
Try like this

Resources