Using hasPermission inside #Query - spring-security

I am trying to annotate a Spring Data Repository findAll() method with a custom #Query annotation. I want to use the Spring Security hasPermission() expression inside the where clause.
I have initialized the SecurityEvaluationContextExtension bean to allow usage of common built-in security expressions in SpEL.
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
I am using a custom PermissionEvaluator implementation.
#Query("select t from #{#entityName} t where 1=?#{hasPermission(filterObject, 'read') ? 1 : 0}")
Page<Stream> findAll(Pageable pageable);
I get below exception. It seems hasPermission is not accessible for some reason. hasRole works just fine.
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'filterObject' cannot be found on object of type 'java.lang.Object[]' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:226)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:94)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:81)
at org.springframework.expression.spel.ast.MethodReference.getArguments(MethodReference.java:155)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:85)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:171)
at org.springframework.expression.spel.ast.Ternary.getValueInternal(Ternary.java:51)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:132)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:297)
at org.springframework.data.jpa.repository.query.SpelExpressionStringQueryParameterBinder.evaluateExpression(SpelExpressionStringQueryParameterBinder.java:139)
What is the correct way to use hasPermission inside a SpEL expression in #Query?

The problem here is that filterObject is in fact the Page instance returned by your repository method
To be able to use it, something like that should be done
#Query("select t from #{#entityName} t where 1=?#{hasPermission(entity, 'read') ? 1 : 0}")
Page<Stream> findAll(Pageable pageable);
where entity is equal to an instance returned by your query.
Sadly, it doesn't seem that it's possible for the moment. I am still searching

Related

Android Dagger/Hilt providers same return value?

I need to know in my Repository whether I have internet connection or not (DB or API), so in my AppModule I use:
#InstallIn(SingletonComponent::class)
#Module
object AppModule {
#Singleton
#Provides
fun provideRepository(api: Api, dao: Dao, internetConn: Boolean) = Repository(api, dao, internetConn)
#Singleton
#Provides
fun provideInternetConnection(#ApplicationContext context: Context): Boolean {
return NetworkUtils().isInternetAvailable(context)
}
}
Hilt knows that the internetConn in the provideRepository params is the fun provideInternetConnection because of the return value (I guess).
But what if I want to create another provider with Boolean return value as well?
It's not working when I create another Singleton provider with Boolean return value.
I suggest you not inject something that might change over time. Instead inject the this class that performs this check and call the function where needed. What you have now, will check for network only once while building the dependency graph.
However it is possible to get a new value is if you inject Provider<Boolean> instead the boolean on its own. Provider will always evaluate the function that is providing the instance, there fore you'll have new value each call of isInternetAvaialbleProvider.get() is called.

Security Context in terms of QueryDslPredicateExecutor and Spring Data Rest

I'm building REST API on the top of Spring Data Rest. Initially all repositories where extending JpaRepository. Lately decision has been made to take a more flexible approach and use QueryDslPredicateExecutor<T> along with QuerydslBinderCustomizer<Q>.
Pretty much all findAll methods exposed in repositories should address two scenarios
principal has a role ROLE_ADMIN then no filtering should be applied a part from Pageable,Sort
principal does not have a role ROLE_ADMIN I would return only those entities which belong to the current user
Getting that done was as simple as annotating findAll method as below.
#Query("select e from Entity e where e.field = ?#{principal} or 1=?#{hasRole('ROLE_ADMIN') ? 1 : 0}")
Page<Entity> findAll(Pageable pageable);
Now I want our findAll to be something similar to below
Page<Entity> findAll(Predicate predicate, Pageable pageable)
Predicate is being build from request parameters(courtesy of #QuerydslPredicate) and is being passed in to RepositoryEntityController which is all being managed by spring-data-rest which is great.
#ResponseBody
#RequestMapping(value = BASE_MAPPING, method = RequestMethod.GET)
public Resources<?> getCollectionResource(#QuerydslPredicate RootResourceInformation resourceInformation,
DefaultedPageable pageable, Sort sort, PersistentEntityResourceAssembler assembler)
throws ResourceNotFoundException, HttpRequestMethodNotSupportedException {
I want to tweak that predicate(2 scenarios as above that I want to address).
It would be something simialr to below.
BooleanBuilder builder = new BooleanBuilder(predicateBuildFromHttpRequest);
builder.and(predicateAddressingOurRequirements);
builder.getValue();
#PostFilter won't be an option as return type for all repos is Page<Entity>.
Use case that I want to address seems to be quite common to me. Having said that I had a look at spring-data and spring-data-rest documentation and could not find anything related to my question.
Question is : Am I missing something obvious here and there is a quick win for it? or I would need to implement custom solution myself? Any comments very much appreciated!
The Querydsl predicates are constructed by QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver which is sadly package private and can't be directly extended.
However, you can make a copy of that, add your security predicate logic and then drop in your implementation instead of the former resolver.
public class MyQueryDslRootResourceArgumentResolver extends RootResourceInformationHandlerMethodArgumentResolver {
// the most of the code is ommitted, the content is identical with
// QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver,
// the important part is postProcessMethod where you can modify the predicate
#Override
#SuppressWarnings({"unchecked"})
protected RepositoryInvoker postProcess(MethodParameter parameter, RepositoryInvoker invoker,
Class<?> domainType, Map<String, String[]> parameters) {
Object repository = repositories.getRepositoryFor(domainType);
if (!QueryDslPredicateExecutor.class.isInstance(repository)
|| !parameter.hasParameterAnnotation(QuerydslPredicate.class)) {
return invoker;
}
ClassTypeInformation<?> type = ClassTypeInformation.from(domainType);
QuerydslBindings bindings = factory.createBindingsFor(null, type);
// modify your predicate here
Predicate predicate = predicateBuilder.getPredicate(type, toMultiValueMap(parameters), bindings);
return new QuerydslRepositoryInvokerAdapter(invoker, (QueryDslPredicateExecutor<Object>) repository, predicate);
}
}
Then add you own configuration class with the custom resolver implementation.
public class CustomRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
#Autowired
ApplicationContext applicationContext;
#Override
public RootResourceInformationHandlerMethodArgumentResolver repoRequestArgumentResolver() {
QuerydslBindingsFactory factory = applicationContext.getBean(QuerydslBindingsFactory.class);
QuerydslPredicateBuilder predicateBuilder = new QuerydslPredicateBuilder(defaultConversionService(),
factory.getEntityPathResolver());
return new MyQueryDslRootResourceArgumentResolver(repositories(),
repositoryInvokerFactory(defaultConversionService()), resourceMetadataHandlerMethodArgumentResolver(),
predicateBuilder, factory);
}
}
Here is an example project that modifies the Predicate (that is produced by the parameters from url) before passing it to the repository.
The demonstration of what David Siro explained above
https://github.com/yeldarxman/QueryDslPredicateModifier

e4 dependency reinjection order: field vs. method

I was quite surprised to see that there is no deterministic behavior for the order in which objects get reinjected.
public class Test {
#Inject private Boolean testBool;
#Inject
public void checkNewObject(Boolean testBoolNew) {
if (!testBoolNew.equals(this.testBool)) {
System.out.println("Out of sync!");
} else {
System.out.println("In sync!");
}
}
}
And this is how I use the class:
context.set(Boolean.class, new Boolean(true));
Test test = ContextInjectionFactory.make(Test.class, context);
context.set(Boolean.class, new Boolean(false));
So, sometimes I get the output:
In sync!
In sync!
And sometimes I get:
In sync!
Out of sync!
Is this really non deterministic or am I just overseeing something?
The documentation clearly states that the injection order should be:
Constructor injection: the public or protected constructor annotated with #Inject with the greatest number of resolvable arguments is selected
Field injection: values are injected into fields annotated with #Inject and that have a satisfying type
Method injection: values are injected into methods annotated with #Inject and that have satisfying arguments
See: https://wiki.eclipse.org/Eclipse4/RCP/Dependency_Injection#Injection_Order
I'm not sure, why this doesn't work as expected in your case.
How is equals() implemented in MyContent?
Is MyContent annotated with #Creatable and or #Singleton?
As a side note: Is this a practical or just an academic problem? Why is it necessary to inject the same instance into a field and into a method on the same target-instance? If you want to have a field variable to cache the value, you can set this from the method.
If you feel this is a bug, please file it here: https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Platform

How to clear SessionMap() in JSF without getting null in reference to this map

I use tomcat 7.0 and JSF 2.1 and I have problem when I call in my .xhtml page something like that: #{homePage.get("userName")}
I get javax.el.ELException: Caused by: java.lang.NullPointerException
at mainPacket.HomePageBean.get(HomePageBean.java:35)
I have ManagedBean like below:
#ManagedBean(name = "homePage")
#ViewScoped
public class HomePageBean {
private Map<String, Object> map;
public HomePageBean() {
map= FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
//remove unnecessary values from sessionMap
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().clear();
}
public String get(Object s){
return map.get(s).toString();
}
}
When I don't use clear, everything works ok. But I want to clear sessionMap. How to resolve it ?
Thanks
Java is an object oriented language. It doesn't give you a copy of the object everytime you request it. No, it gives you a reference to the object instance in memory. At the moment you invoke Map#clear() on the session map, then the map reference which you obtained just beforehand is basically also emptied, because it points to exactly the same map instance which you just emptied!
Your concrete functional requirement is nowhere mentioned in the question and the whole design in the code posted so far makes honestly no utter sense (I can't imagine any sensible real world use case for this), so it's hard to propose you the right solution. Best what you can get is the advice to add a nullcheck.
public String get(Object s){
Object value = map.get(s);
return (value != null) ? value.toString() : null;
}
You should by the way be very careful with abruptly emptying the session map this way. JSF stores view scoped and session scoped managed beans in there and it's also used by the flash scope.

Is there any way in Struts 2 by which I can get all namespaces in my app?

Is there any way in Struts2 by which I can get list of namespaces in my App ?
I want this as set or list at runtime .
I am using Struts2 RestActionMapper plugin.
When there invalid namespace is specified for valid action, Struts is throwing namespace error.
But I could not redirected to standard error page when this error occurs. I tried almost all options e.g.global error mapping default namespace etc . Nothing worked. So thought it would be great if I could get list of namespaces in my app, thus i could have checked invalid namespace against my list of valid namespaces and accordingly I could have thrown generic error which would finally result in my standard error page.
I am looking for how to get list of all namespaces in my project.
So basically I want to do something like this.
validNamespaces = getNamespaces();
if(validNamespaces.contains(namespaceRetrivedFromRestPlugin))
{Sysout("This is valid namespace.")}
else
{Sysout("Invalid namespace");}
This is possible, though like Steven has pretty much stated, I'm not convinced that this is the right approach to the problem you state of redirecting to an error page. But, I'll leave that part up to you and use this space to answer the namespace question.
This code will have to be in a Struts2-created object for the injection to work.
private Configuration configuration;
#Inject
public void setConfiguration(Configuration config) {
this.configuration = config;
}
protected Set<String> getNamespaces() {
Set<String> namespaces = Collections.emptySet();
Map<String, Map<String, ActionConfig>> allActionConfigs = this.configuration.getRuntimeConfiguration().getActionConfigs();
if (allActionConfigs != null) {
namespaces = allActionConfigs.keySet();
}
return namespaces;
}
The configuration can also be obtained from a ConfigurationManager. Also, you would obviously want to store these in a variable rather than calling above method over and over. If your object is, say, an interceptor, then you could call this method from the init() method and store it in a class-level variable.

Resources