I was trying to simplify the code of security checks in my grails app and I found that there is a way to drive the security on a service class.
Some of the references I found related to that:
https://www.mscharhag.com/grails/spring-security-call-bean-method-in-spel-expression
Grails custom security evaluator
and some others...
So I tried wiring everything in and seems pretty straightforward, but when I am configuring my custom beans into resources.groovy I am getting this error.
A component required a bean named 'parameterNameDiscoverer' that could not be found.
My resources.groovy looks like this:
import com.auth0.client.auth.AuthAPI
import grails.plugin.springsecurity.rest.RestAuthenticationProvider
import priz.auth0.Auth0APIService
import priz.auth0.Auth0TokenStorageService
import priz.auth0.Auth0TokenVerificationService
import priz.auth0.Auth0UserResolverService
import priz.security.GrailsBeanResolver
import priz.security.GrailsExpressionHandler
import priz.security.UserPasswordEncoderListener
// Place your Spring DSL code here
beans = {
expressionHandler(GrailsExpressionHandler) {
beanResolver = ref('beanResolver')
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
beanResolver(GrailsBeanResolver) {
grailsApplication = ref('grailsApplication')
}
userPasswordEncoderListener(UserPasswordEncoderListener)
authApi(AuthAPI) { beanDefinition ->
beanDefinition.constructorArgs = [
'${priz.auth0.api.domain}',
'${priz.auth0.api.clientId}',
'${priz.auth0.api.clientSecret}'
]
}
auth0APIService(Auth0APIService) {
authAPI = ref('authApi')
}
auth0TokenVerificationService(Auth0TokenVerificationService)
auth0UserResolverService(Auth0UserResolverService)
tokenStorageService(Auth0TokenStorageService) {
jwtService = ref('jwtService')
userDetailsService = ref('userDetailsService')
auth0TokenVerificationService = ref('auth0TokenVerificationService')
auth0APIService = ref('auth0APIService')
auth0UserResolverService = ref('auth0UserResolverService')
}
/* restAuthenticationProvider */
restAuthenticationProvider(RestAuthenticationProvider) {
tokenStorageService = ref('tokenStorageService')
useJwt = false
jwtService = ref('jwtService')
}
}
Of course, I don't have parameterNameDiscoverer specifically defined in the resources, but I expected that since I didn't customize any of these dependencies are already provided by the Spring Security plugins. But it seems like they cannot be found.
What am I missing? Do I need to define the entire dependency tree in resources?
This should be as simple as creating the bean instance of the implementation you want by adding something like the following to your resources.groovy
parameterNameDiscoverer(DefaultSecurityParameterNameDiscoverer)
Assuming you want the default implementation that is provided with Spring Security (v3.2+). It's not clear what implementation you want, so you may browse the java docs.
Related
Not sure if I'm thinking right about this, I'm looking[in CDI] for something similar to what we have in Spring - #ConditionalOnMissingBean - that allows you tell spring - create only if the bean specified is missing.
I've tried using extensions, looks like one can tap several events, and use those to VETO beans. One way might be to have BeanManager at this stage, and look for already present beans, and if it contains the one you're about to inject, VETO this one. BUT, this would only work when we HAVE LOOKED AT ALL the beans.
AfterBeanDiscovery looks suitable, however, before it is invoked, validation fails, complaining of multiple beans of the same type.
Would be great if I could get some help here.
Your question is interesting and can be solved using a CDI extension (almost as you describe, actually), see below for a naive, working, proof-of-concept implementation. It is naive because it does not handle e.g. producer methods/fields and may be missing more.
CDI extensions are really great and powerful, but can be rather technical, so let's discuss other alternatives first.
Specialization: Maybe it is enough for your use case to document explicitly that you provide the default implementation of SomeService through, say, public class SomeServiceDefaultImpl and in order to override it the developer should do:
#Specializes
public class SomeServiceSpecialImpl extends SomeServiceDefaultImpl {...}
Also consider the alternatives, as mentioned in the comment from John Ament.
Qualifiers: If this service is used only in one place/a few places and only inside your code, you could qualify your SomeServiceDefaultImpl with a custom qualifier, say #MyDefaultImpl. Then inject an Instance<SomeService>, look for an unqualified instance first and, if that is not satisfied, look for the qualified - something along the lines of:
private SomeService someService;
#Inject
void setSomeServiceInstance(Instance<SomeService> s) {
// not tried, please adapt as needed
if( s.isUnsatisfied() ) {
someService = s.select(new MyDefaultImplAnnotation()).get();
}
else {
someService = s.get();
}
}
Provide a default implementation that is #Vetoed so as to force the client of your code to provide an implementation. If the client wants to use the default, they can simply use a producer.
Having said the above, the implementation below is a proof of concept that:
Requires the following annotation to be present on the default implementation:
#Target({ TYPE, METHOD, FIELD })
#Retention(RUNTIME)
#Documented
public #interface ConditionalOnMissingBean {
Class<?> value();
}
The value() is required and denotes the bean type that is "defaulted". Your implementation can be smarter, i.e. detect the bean type from the actual default implementation, but, hey, that's only a proof of concept!
Blatantly ignores producers!
Is lightly tested, so there are probably evil corner cases, so BEWARE!
In addition to the code you need all the choreography of an extension (META-INF/services/javax.enterprise.inject.spi.Extension, beans.xml).
import java.util.HashMap;
import java.util.Map;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanAttributes;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionTargetFactory;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
public class ConditionalOnMissingBeanExtension implements Extension {
private Map<Class<?>, AnnotatedType<?>> map = new HashMap<>();
<T> void processAnnotatedType(#Observes ProcessAnnotatedType<T> pat) {
AnnotatedType<?> annotatedType = pat.getAnnotatedType();
ConditionalOnMissingBean annotation = annotatedType.getAnnotation(ConditionalOnMissingBean.class);
if( annotation != null ) {
map.put(annotation.value(), annotatedType);
pat.veto();
}
}
void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager beanManager) {
map.entrySet().stream()
.filter(e -> doesNotHaveBeanOfType(beanManager, e.getKey()))
.map(e -> defineBean(beanManager, e.getValue()))
.forEach(abd::addBean);
map = null;
}
private boolean doesNotHaveBeanOfType(BeanManager beanManager, Class<?> type) {
return beanManager.getBeans(type).isEmpty();
}
private <T> Bean<T> defineBean(BeanManager beanManager, AnnotatedType<T> annotatedType) {
BeanAttributes<T> beanAttributes = beanManager.createBeanAttributes(annotatedType);
InjectionTargetFactory<T> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType);
return beanManager.createBean(beanAttributes, annotatedType.getJavaClass(), injectionTargetFactory);
}
}
An example of a default implementation of a service interface would be:
#ApplicationScoped
#ConditionalOnMissingBean(SomeService.class)
public class SomeServiceDefaultImpl implements SomeService {
#Override
public String doSomeCalculation() {
return "from default implementation";
}
}
Grails 2.4.x here. If I created a Grails service using grails create-service com.example.Widget, then how can I inject a reference of that service (a "bean") into a class under src/groovy?
This is actually not explained anywhere in the official Grails docs, and extensive searching turned back nadda.
1) You can use Spring Beans to inject a service into a non-artefact groovy file, using resources.groovy:
MyClass.groovy
class MyClass {
def widgetService
...
}
resources.groovy
beans = {
myclass(com.example.MyClass) {
widgetService = ref('widgetService')
}
}
2) There is also an additional #Autowired annotation that can do the same thing:
MyClass.groovy
import org.springframework.beans.factory.annotation.Autowired
class MyClass {
#Autowired
def widget
...
}
resources.groovy
beans = {
myclass(com.example.MyClass) {}
}
Notice - this time the myclass bean doesn't need the reference to the widget.
3) There is an alternative to injecting the WidgetService - using the Holders class to get the grailsApplication which will have a reference to the existing bean.
import grails.util.Holders
class MyClass {
def widgetService = Holders.grailsApplication.mainContext.getBean('widgetService')
...
}
**Update**
4) There is another option that is a hybrid of 1) and 2) -
Having the bean(s) injected by autowire=true within resources.groovy:
MyClass.groovy
class MyClass {
def widgetService
...
}
resources.groovy
beans = {
myclass(com.example.MyClass) { bean ->
bean.autowire = true
}
}
This is the approach I've been using locally as I feel it's the cleanest, but it does take more advantage of Grail's 'magic' (for better or worse).
I'd like to add custom SpEL methods in Grails applciation, like it's done for plain Spring-Security application in this question, by overriding EvaluationContext. Will this work?
How do I plug global-method-security into security config? I can configure security, but what to add there? Something like
grails.plugins.springsecurity = {
'global-method-security' {
'expression-handler' {
ref("myMethodSecurityExpressionHandler")
}
}
}
? But what code will interpret it?
Looking into SpringSecurityCoreGrailsPlugin.groovy also gives me no insights.
This is only available if you also have the spring-security-acl plugin installed. It configures the expressionHandler bean:
expressionHandler(DefaultMethodSecurityExpressionHandler) {
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
So if you have your own subclass of DefaultMethodSecurityExpressionHandler you can replace the bean in resources.groovy like this:
import com.mycompany.myapp.MyMethodSecurityExpressionHandler
beans = {
expressionHandler(MyMethodSecurityExpressionHandler) {
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
}
I am currently updating my Grails project in order not to use the deprecated ConfigurationHolder class.
This goes fine in most cases, but I am facing trouble in my custom codec classes, where I have been using the following approach until now:
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
class MyCodec {
static boolean myStaticConfigProperty=CH.config.myStaticConfigProperty
static encode = { something ->
if(myStaticConfigProperty)
...
}
}
Direct injection using
def grailsApplication
does not work in this case since this will be injected as a non-static object.
Instead I have tried to use the approach suggested in this post getting-grails-2-0-0m1-config-info-in-domain-object-and-static-scope, but I cannot make it work even after injecting the grailsApplication object into my codec metaclasses in the bootstrap:
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.codecClasses) {
cc.clazz.metaClass.getGrailsApplication = { -> grailsApplication }
cc.clazz.metaClass.static.getGrailsApplication = { -> grailsApplication }
}
}
}
Could anyone suggest an approach that will allow me to access the config object in a static way inside codec classes?
I'd suggest something like this completely untested code:
class MyCodec {
static def grailsConfig
static boolean myStaticConfigProperty = grailsConfig.myStaticConfigProperty
static encode = { something ->
if(myStaticConfigProperty)
...
}
}
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.codecClasses) {
cc.grailsConfig = grailsApplication.config
}
}
}
If all of your codec classes just need the same one configuration property, you could skip injecting the grailsApplication and/or the config object entirely, and just set the one static property from BootStrap.
it works for me in grails 2.2.3
import grails.util.Holders as holders;
class MyFileCodec {
static encode = {file ->
def configPath= holders.grailsApplication.config.share.contextPath
return "${configPath}/${file.name}"
}
}
grails.util.Holders has been introduced since grails 2.0, it's the way to access config object.
I'm having trouble accessing springSecurityService from resources.groovy file, I'm trying to load user locale setting and create LocaleResolver
import User
beans = {
localeResolver(org.springframework.web.servlet.i18n.SessionLocaleResolver) {
def user = User.get(springSecurityService.principal.id)
if (user?.settings?.locale) {
defaultLocale = new Locale(user?.settings?.locale)
java.util.Locale.setDefault(defaultLocale)
}
}
}
Thanks,
Mika
Your code above doesn't make a lot of sense. In resources.groovy you're supposed to define the implementation class of Spring beans and set their dependencies. It looks like you're trying to actually write the implementation class in resources.groovy.
Instead you should write your own LocaleResolver class
package org.example
class MyLocaleResolver extends AbstractLocaleResolver {
def springSecurityService
// implementation of methods omitted, because I haven't clue how you want to resolve Locales
}
Then in resources.groovy, define a bean of this type that replaces the default localeResolver bean
beans = {
localeResolver(org.example.MyLocaleResolver) {
springSecurityService = ref('springSecurityService')
}
}