I'm trying to call a custom method using Spring #PreAuthorize at the method level in the service layer, but my custom method is not being called. My code:
spring-application-context.xml:
<context:component-scan base-package="com.dice" />
<sec:global-method-security pre-post-annotations="enabled"/>
Component definition:
#Component(value="authorizationService")
public class AuthorizationService implements AuthorizationServiceInt {
public boolean test(String key) {
logger.debug("AUTHORIZATION CALLED: " + key);
return false;
}
}
Controller that calls the util class:
#InjectParam
CustomerUtil customerUtil;
#GET
#Path("/{customerId}/")
#Produces(MediaType.APPLICATION_JSON)
public Response getCustomerInfo(#PathParam("customerId") Integer customerId,
#DefaultValue("") #QueryParam("fields") String partialResponseFields) {
logger.debug("getCustomerInfo called: id: " + customerId + ", uriInfo = " + uriInfo.getPath());
try {
return buildOKResourceResponse(new CustomerActionResource(customerUtil.getCustomerInfo(uriInfo, customerId)),partialResponseFields);
Usage of the component:
#InjectParam
AuthorizationService svc;
#PreAuthorize("#authorizationService.test('special')")
public Customer getCustomerInfo(UriInfo uriInfo, Integer id) {
logger.debug("svc: " + svc.hasPermission("x"));
return populateCustomerInfo(uriInfo, PstUserModelServiceUtil.getPstUserModelByPstUserId(id));
}
I included the InjectParam just to make sure that the AuthorizationService class was being recognized by Spring. And sure enough, the 'AUTHORIZATION CALLED' debug statement appears when I call via the injected class, but does not appear when called via the #PreAuthorization call.
My spring jars:
spring-aop-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-context-support-3.2.2.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-data-commons-1.5.0.RELEASE.jar
spring-data-solr-1.0.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
spring-jdbc-3.2.0.RELEASE.jar
spring-orm-3.2.0.RELEASE.jar
spring-security-acl-3.1.4.RELEASE.jar
spring-security-config-3.1.4.RELEASE.jar
spring-security-core-3.1.4.RELEASE.jar
spring-security-oauth2-1.1.0.BUILD-SNAPSHOT.jar
spring-security-taglibs-3.1.4.RELEASE.jar
spring-security-web-3.1.4.RELEASE.jar
spring-tx-3.2.0.RELEASE.jar
spring-web-3.2.2.RELEASE.jar
spring-webmvc-3.2.2.RELEASE.jar
What am I doing wrong? I have interfaces on both the component class and the class using the component as I read that with aop, the fact that Spring decorates the impl class that an interface is needed for spring to see the underlying implementation.
Spring Security works by creating a proxy of the class that is annotated with the security annotation (i.e. #PreAuthorize). In order for it to be proxied, the class that has the security annotation must be created by Spring and any invocations must be made on the instance created by Spring.
Verify that the class with public Customer getCustomerInfo is created by Spring and not in some other way.
Related
I have an Azure Functions project that leverages Dependency Injection (Startup.cs injects services based on the different interfaces). Those services that implement the interfaces are using constructor dependency injection as well.
In one of those implementations, I want to call a method on a Durable Entity, but I prefer not to make the DurableEntityClient part of the method signature (as other implementations might not need the EntityClient at all). So therefore, I was hoping to see that IDurableEntityClient injected in the constructor of my class.
But it turns out the value is null. Wondering if this is something that is supported and feasible? (to have a DI-friendly way of injecting classes that want to get the EntityClient for the Functions runtime they are running in)
Some code snippets:
Startup.cs
builder.Services.AddSingleton<IReceiver, TableReceiver>();
Actual Function
public class ItemWatchHttpTrigger
{
private IReceiver _receiver;
public ItemWatchHttpTrigger(IReceiver receiver)
{
_receiver = receiver;
}
[FunctionName("item-watcher")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "item/{itemId}")]
HttpRequest request, string itemId, [DurableClient] IDurableEntityClient client, ILogger logger)
{
// Actual implementation
}
}
Referenced class
public class TableReceiver : IReceiver
{
private IDurableEntityClient _entityClient;
public TableReceiver(IDurableEntityClient client)
{
_entityClient = client; // client is null :(
}
}
Based on the answer of my github issue, it seems it is possible to inject this in Startup, since the 2.4.0 version of the Microsoft.Azure.WebJobs.Extensions.DurableTask package:
Some code snippets:
Startup.cs
builder.Services.AddSingleton<IReceiver, TableReceiver>();
builder.Services.AddDurableClientFactory();
Referenced class
public class TableReceiver : IReceiver
{
private IDurableEntityClient _entityClient;
public TableReceiver(IDurableClientFactory entityClientFactory, IConfiguration configuration)
{
_entityClient = entityClientFactory.CreateClient(new DurableClientOptions
{
TaskHub = configuration["TaskHubName"]
});
}
}
Github issue
I have a class (OmeletteMaker) that contains an injected field (Vegetable). I would like to write a producer that instantiates an injected object of this class. If I use 'new', the result will not use injection. If I try to use a WeldContainer, I get an exception, since OmeletteMaker is #Alternative. Is there a third way to achieve this?
Here is my code:
#Alternative
public class OmeletteMaker implements EggMaker {
#Inject
Vegetable vegetable;
#Override
public String toString() {
return "Omelette: " + vegetable;
}
}
a vegetable for injection:
public class Tomato implements Vegetable {
#Override
public String toString() {
return "Tomato";
}
}
main file
public class CafeteriaMainApp {
public static WeldContainer container = new Weld().initialize();
public static void main(String[] args) {
Restaurant restaurant = (Restaurant) container.instance().select(Restaurant.class).get();
System.out.println(restaurant);
}
#Produces
public EggMaker eggMakerGenerator() {
return new OmeletteMaker();
}
}
The result I get is "Restaurant: Omelette: null", While I'd like to get "Restaurant: Omelette: Tomato"
If you provide OmeletteMaker yourself, its fields will not be injected by the CDI container. To use #Alternative, don't forget specifying it in the beans.xml and let the container instantiate the EggMaker instance:
<alternatives>
<class>your.package.path.OmeletteMaker</class>
</alternatives>
If you only want to implement this with Producer method then my answer may be inappropriate. I don't think it is possible (with standard CDI). The docs says: Producer methods provide a way to inject objects that are not beans, objects whose values may vary at runtime, and objects that require custom initialization.
Thanks Kukeltje for pointing to the other CDI question in comment:
With CDI extensions like Deltaspike, it is possible to inject the fields into an object created with new, simply with BeanProvider#injectFileds. I tested this myself:
#Produces
public EggMaker eggMakerProducer() {
EggMaker eggMaker = new OmeletteMaker();
BeanProvider.injectFields(eggMaker);
return eggMaker;
}
The various #EnableXXXRepository annotations of Spring Data allow you to specify a custom base class for your repositories, which will be used as an implementation of methods in your repository.
If such a base class needs access to other beans in the ApplicationContext how does one get those injected? It doesn't work out of the box, because Spring Data instantiates those base classes itself, supporting only special store dependent constructor parameters.
Note: I created this answer in the chat to this now deleted question and thought it might be valuable for others, although the original question is gone.
In the #Enable...Repository annotation specify a repositoryBaseClass and repositoryFactoryBeanClass. Like this:
#EnableMongoRepositories(
repositoryBaseClass = MyBaseClass.class,
repositoryFactoryBeanClass = MyRepositoryFactoryBean.class)
In that RepositoryFactoryBean class, you can use normal dependency injection, because it is a Spring Bean, so, for example, you can get an instance of SomeBean injected via the constructor, as shown below:
public class MyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends MongoRepositoryFactoryBean<T,S,ID>{
private final SomeBean bean;
public MyRepositoryFactoryBean(Class repositoryInterface, SomeBean bean) {
super(repositoryInterface);
this.bean = bean;
}
}
Your RepositoryFactoryBean now create an instance of a custom RepositoryFactory by overwriting 'getFactoryInstance'.
#Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new MyMongoRepositoryFactory(operations, bean);
}
While doing so, it can pass on the bean to be injected. bean in the example above.
And this factory finally instantiates your repository base class. Probably the best way to do it is to delegate everything to the existing factory class and just add injecting of the dependency to the mix:
public class MyMongoRepositoryFactory extends MongoRepositoryFactory {
private final SomeBean bean;
MyMongoRepositoryFactory(MongoOperations mongoOperations, SomeBean bean) {
super(mongoOperations);
this.bean = bean;
}
#Override
protected Object getTargetRepository(RepositoryInformation information) {
Object targetRepository = super.getTargetRepository(information);
if (targetRepository instanceof MyBaseClass) {
((MyBaseClass) targetRepository).setSomeBean(bean);
}
return targetRepository;
}
}
There is a complete working example on Github.
In my JSF application I'm using a #ViewScoped bean Publication to show/edit data coming from my database. In that bean there is a field for a subtype-specific data object, i.e. containing a different object depending on whether the publication is, say, a book or an article.
#ViewScoped
#Named
public class Publication implements Serializable {
#Inject
DatabaseStorage storage;
...
String id;
String type;
PublicationType typedStuff;
#PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = new Article(id);
break;
case "BOOK":
typedStuff = new Book(id);
break;
default:
break;
}
}
}
...with classes Article and Book that implement / extend PublicationType.
So far, so good, but I would like for typedStuff to be a CDI bean, so that I can inject useful resources there.
I've read this and this page on producer methods, as well as this tutorial and this very related SO question, but none of them answer precisely my question: Can I inject based on a field that the injecting bean itself only knows at runtime?
I've gotten the producer method to work as such, but I can't parametrize it, so I can't get that switch to work.
If I put the producer method in a separate class (or bean) then I don't have access to the type field.
If I inject the injecting bean into the producer class, or move the producer method into the injecting class, I get a circular injection.
If I put the producer method statically into the injecting class, I also don't have access, because type cannot be static. (Although, since it's only used momentarily...?)
Also (and that is probably the answer right there), the producer method is executed before my injecting bean's init method, so type wouldn't even have been set yet.
Does anybody have a better idea?
No you cannot, but you can select a bean based on the field value. Say:
public interface PublicationType {}
#PType("ARTICLE")
public class Article implements PublicationType{}
#PType("BOOK")
public class Book implements PublicationType {}
And define a qualifier:
public #interface PType {
String value();
}
And define an AnnotationLiteral:
public class PTypeLiteral extends AnnotationLiteral<PType> implements PType {}
Then you can use:
public class Publication {
#Any
#Inject
private Instance<PublicationType> publicationTypes;
public void doSomething() {
PType ptype = new PTypeLiteral(type);
// Of course you will have to handle all the kind of exceptions here.
PublicationType publicationType = publicationTypes.select(ptype).get();
}
}
There is the javax.inject.Provider interface (I think You are using #Named and #Inject annotations from the same package).
You could use it to achieve what You want. It will create instances for You with injected fields.
One drawback is that You will have to set the id yourself.
#ViewScoped
#Named
public class Publication implements Serializable {
#Inject
DatabaseStorage storage;
#Inject
Provider<Article> articleProvider;
#Inject
Provider<Book> bookProvider;
String id;
String type;
PublicationType typedStuff;
#PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = articleProvider.get();
typedStuff.setId(id);
break;
case "BOOK":
typedStuff = bookProvider.get();
typedStuff.setId(id);
break;
default:
break;
}
}
}
I have service interface
public interface CompoundService<T extends Compound> {
T getById(final Long id);
//...
}
and abstract implementation
public abstract class CompoundServiceImpl<T extends Compound>
implements CompoundService<T> {
//...
private Class<T> compoundClass;
//...
}
Every implementation of Compound requires it's own service interface which extends CompoundService and it's own service class which extends CompoundServiceImpl.
I would now like to add basic security uisng annotations to my methods in CompoundService. As far as I understood I must add them in the interface not the actual implementation. Since a user can have different roles for different implementations of Compound, i must take this into account. Meaning in #PreAuthorize I would like to get the name of the Compound implementation, eg. compoundClass.getSimpleName(). So that I get something like:
public interface CompoundService<T extends Compound> {
#PreAuthorize("hasRole('read_' + #root.this.compoundClass.getSimpleName())")
T getById(final Long id);
//...
}
This is basically what is mentioned here:
https://jira.springsource.org/browse/SEC-1640
however there is no example and I did not really get the solution. So should i use this? or as above #root.this?
My second question is, since this is in an interface which will be implemented by a proxy (from spring) will the experession this.compoundClass actually evaluate properly?
And last but not least how can I actually test this?*
*
I'm not actually creating a finished application but something configurable, like a framework for s specific type of database search. Meaning most authorization and authentication stuff has to come from the implementer.
Unit Testing
see http://www.lancegleason.com/blog/2009/12/07/unit-testing-spring-security-with-annotations
Since that is an old tutorial you might need to change the referenced schema versions. But more importantly the SecurityContext.xml configuration shown there does not work with Spring Security 3. See Spring Security - multiple authentication-providers for a proper configuration.
I did not require the mentioned dependencies:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core-tiger</artifactId>
</dependency>
it worked without them (however did not create an abstract test class)
root.this
This is in fact correct approach
The problem is that you can't use getSimpleName() of a class parameter. For an in-depth discussion see http://forum.springsource.org/showthread.php?98570-Getting-Payload-Classname-in-Header-Enricher-via-SpEL
The workarounds shown there did not help me much. So I came up with this very simple solution:
Just add the string property String compoundClassSimpleName to CompoundServiceImpl and set it in the constructor (which is called by subclasses):
Public abstract class CompoundServiceImpl<T extends Compound>
implements CompoundService<T> {
private String compoundClassSimpleName;
//...
public ChemicalCompoundServiceImpl(Class<T> compoundClass) {
this.compoundClass = compoundClass;
this.compoundClassSimpleName = compoundClass.getSimpleName();
}
//...
public String getCompoundClassSimpleName(){
return compoundClassSimpleName;
}
}
and her a Service implementing above abstract service:
public class TestCompoundServiceImpl extends CompoundServiceImpl<TestCompound>
implements TestCompoundService {
//...
public TestCompoundServiceImpl() {
super(TestCompound.class);
}
//...
}
And final the #PreAuthorize annotation usage:
public interface CompoundService<T extends Compound> {
#PreAuthorize("hasRole('read_' + #root.this.getCompoundClassSimpleName())")
public T getById(final Long id);
}
For above example the expression will evaluate to a role named "read_TestCompound".
Done!
As often the solution is very simple but getting there is a PITA...
EDIT:
for completeness the test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:ApplicationContext.xml",
"classpath:SecurityContext.xml"
})
public class CompoundServiceSecurityTest {
#Autowired
#Qualifier("testCompoundService")
private TestCompoundService testCompoundService;
public CompoundServiceSecurityTest() {
}
#Before
public void setUp() {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("user_test", "pass1"));
}
#Test
public void testGetById() {
System.out.println("getById");
Long id = 1000L;
TestCompound expResult = new TestCompound(id, "Test Compound");
TestCompound result = testCompoundService.getById(id);
assertEquals(expResult, result);
}
}